--[[
File: aadp_ip.lua
----  License  ----
This protocol dissector is written in the script language Lua.
Lua is Copyright (C) 1994-2008 Lua.org, PUC-Rio.
See http://www.lua.org for more information about Lua.

The actual content, AADP IP, is created and owned by ASSA ABLOY Shared Technologies.

This script is intended to be read by the software Wireshark v2 and up.
See https://www.wireshark.org/ for more info.
----end License----

Description: This file dissects packages sent with the protocol AADP (ASSA ABLOY Device Protocol).
Version: see my_info at end of file
Authors: Stefan Johansson [stefan.johansson2@assaabloy.com]
         Gustav Blix [gustav.blix@assaabloy.com]

based on a dissector written by
Kim Bäckström <kim.backstrom@gmail.com>
]]--

-- Defines and ENUMs
PROTO_TAG_AADP = "AADP_IP"
AADP_PORT      = 9999
PROTOCOL_DATA_UNIT_MAX_LENGTH_THEORETICAL = 32767
PROTOCOL_DATA_UNIT_MAX_LENGTH = PROTOCOL_DATA_UNIT_MAX_LENGTH_THEORETICAL -- max length just set well above largest known message size around 1500
PROTOCOL_DATA_UNIT_MIN_LENGTH = 1
HIGHEST_PDU_ID                = 127
LOWEST_PDU_ID                 = 1
SEC_FROM_1970_TO_2000         = 946684800
HIGHLIGHT_ALL                 = -1
OFFSET_ZERO                   = 0

ENUM_LEN             = 1
SHORT_LEN            = 2
BOOL_LEN             = 1
BYTE_LEN             = 1
PARAMETER_TYPE_LEN   = 1
PDU_LEN_LEN          = SHORT_LEN -- length of PDU length field is 2 bytes
PDU_ID_LEN           = 1
DEVICE_ID_LEN        = 12 --device id is 12 bytes
STATUS_LEN           = 1
STR_LEN_LEN          = 1 -- length field of string is 1 byte
TIME_ZONE_LEN        = 2
TIME_ZONE_MIN        = (-48)
TIME_ZONE_MAX        = 56
CRED_BLOCK_TYPE_LEN  = 2
CRED_LEN_LEN         = SHORT_LEN
CREDENTIAL_SOURCE_DETAILS_LEN = 2
LOCK_VERSION_LEN     = 1

RES_OK = 0
RES_ERROR = -1

pdu_names = {
    [1] = "HelloRequest",
    [2] = "HelloResponse",
    [3] = "DeviceGroupInfoNotification",
    [4] = "PDU_ID_4_NOT_USED",
    [5] = "CredentialNotification",
    [6] = "KeypadNotification",
    [7] = "RestartCommand",
    [8] = "IndicationCommand",
    [9] = "IndicationResult",
    [10] = "GetSupportedDoorModesCommand",
    [11] = "GetSupportedDoorModesResult",
    [12] = "DoorOpeningCommand",
    [13] = "DoorOpeningResult",
    [14] = "PDU_ID_14_NOT_USED",
    [15] = "SetDoorModeCommand",
    [16] = "SetDoorModeResult",
    [17] = "StatusRequest",
    [18] = "StatusResponse",
    [19] = "GetDeviceParameterCommand",
    [20] = "GetDeviceParameterResult",
    [21] = "DtcNotification",
    [22] = "GetDtcStatusCommand",
    [23] = "GetDtcStatusResult",
    [24] = "ClearDtcCommand",
    [25] = "ClearDtcResult",
    [26] = "GetDeviceListCommand",
    [27] = "GetDeviceListResult",
    [28] = "PDU_ID_28_NOT_USED",
    [29] = "PDU_ID_29_NOT_USED",
    [30] = "GetAuditTrailCommand",
    [31] = "GetAuditTrailResult",
    [32] = "AuditTrailNotification",
    [33] = "PDU_ID_33_NOT_USED",
    [34] = "PDU_ID_34_NOT_USED",
    [35] = "PDU_ID_35_NOT_USED",
    [36] = "PDU_ID_36_NOT_USED",
    [37] = "PDU_ID_37_NOT_USED",
    [38] = "PDU_ID_38_NOT_USED",
    [39] = "PDU_ID_39_NOT_USED",
    [40] = "TimeNotification",
    [41] = "SetTimeCommand",
    [42] = "SetTimeResult",
    [43] = "GetTimeCommand",
    [44] = "GetTimeResult",
    [45] = "PDU_ID_45_NOT_USED",
    [46] = "PDU_ID_46_NOT_USED",
    [47] = "PDU_ID_47_NOT_USED",
    [48] = "PDU_ID_48_NOT_USED",
    [49] = "PDU_ID_49_NOT_USED",
    [50] = "SetDeviceParameterCommand",
    [51] = "SetDeviceParameterResult",
    [52] = "DeviceParameterNotification",
    [53] = "PDU_ID_53_NOT_USED",
    [54] = "PDU_ID_54_NOT_USED",
    [55] = "SetCredentialCommand",
    [56] = "SetCredentialResult",
    [57] = "DeleteCredentialCommand",
    [58] = "DeleteCredentialResult",
    [59] = "CredentialManagementNotification",
    [60] = "PDU_ID_60_NOT_USED",
    [61] = "PDU_ID_61_NOT_USED",
    [62] = "PDU_ID_62_NOT_USED",
    [63] = "PDU_ID_63_NOT_USED",
    [64] = "GetDoorStateCommand",
    [65] = "GetDoorStateResult",
    [66] = "DoorStateNotification",
    [67] = "PDU_ID_67_NOT_USED",
    [68] = "GetLockStateCommand",
    [69] = "GetLockStateResult",
    [70] = "LockStateNotification",
    [71] = "PDU_ID_71_NOT_USED",
    [72] = "GetKeyCylinderStateCommand",
    [73] = "GetKeyCylinderStateResult",
    [74] = "KeyCylinderStateNotification",
    [75] = "PDU_ID_75_NOT_USED",
    [76] = "GetHandleStateCommand",
    [77] = "GetHandleStateResult",
    [78] = "HandleStateNotification",
    [79] = "PDU_ID_79_NOT_USED",
    [80] = "GetTamperStateCommand",
    [81] = "GetTamperStateResult",
    [82] = "TamperStateNotification",
    [83] = "PDU_ID_83_NOT_USED",
    [84] = "GetActivatorStateCommand",
    [85] = "GetActivatorStateResult",
    [86] = "ActivatorStateNotification",
    [87] = "PDU_ID_87_NOT_USED",
    [88] = "GetDoorModeCommand",
    [89] = "GetDoorModeResult",
    [90] = "DoorModeNotification",
    [91] = "PDU_ID_91_NOT_USED",
    [92] = "PDU_ID_92_NOT_USED",
    [93] = "PDU_ID_93_NOT_USED",
    [94] = "PDU_ID_94_NOT_USED",
    [95] = "PDU_ID_95_NOT_USED",
    [96] = "PDU_ID_96_NOT_USED",
    [97] = "PDU_ID_97_NOT_USED",
    [98] = "PDU_ID_98_NOT_USED",
    [99] = "PDU_ID_99_NOT_USED",
    [100] = "PDU_ID_100_NOT_USED",
    [101] = "PDU_ID_101_NOT_USED",
    [102] = "PDU_ID_102_NOT_USED",
    [103] = "PDU_ID_103_NOT_USED",
    [104] = "PDU_ID_104_NOT_USED",
    [105] = "PDU_ID_105_NOT_USED",
    [106] = "PDU_ID_106_NOT_USED",
    [107] = "PDU_ID_107_NOT_USED",
    [108] = "PDU_ID_108_NOT_USED",
    [109] = "PDU_ID_109_NOT_USED",
    [110] = "PDU_ID_110_NOT_USED",
    [111] = "PDU_ID_111_NOT_USED",
    [112] = "PDU_ID_112_NOT_USED",
    [113] = "PDU_ID_113_NOT_USED",
    [114] = "PDU_ID_114_NOT_USED",
    [115] = "PDU_ID_115_NOT_USED",
    [116] = "PDU_ID_116_NOT_USED",
    [117] = "PDU_ID_117_NOT_USED",
    [118] = "PDU_ID_118_NOT_USED",
    [119] = "PDU_ID_119_NOT_USED",
    [120] = "PDU_ID_120_NOT_USED",
    [121] = "PDU_ID_121_NOT_USED",
    [122] = "PDU_ID_122_NOT_USED",
    [123] = "PDU_ID_123_NOT_USED",
    [124] = "PDU_ID_124_NOT_USED",
    [125] = "PDU_ID_125_NOT_USED",
    [126] = "PDU_ID_126_NOT_USED",
    [127] = "PDU_ID_127_NOT_USED",
    [0] = "PDU_ID_0_NOT_USED" 
}

credential_block_type = {
    [0x0000] = "UNKNOWN" ,
    [0x0010] = "CUSTOM" ,
    [0x0100] = "SOURCE_DETAILS" ,
    [0x0200] = "MIFARE_UID" ,
    [0x0201] = "DYNAMIC_DATA" ,
    [0x0300] = "ICLASS_ACCESS_ID" ,
    [0x0400] = "ISO14443B_UID" ,
    [0x0500] = "ISO15693_UID" ,
    [0x0600] = "LEGIC_UID" ,
    [0x0700] = "SIO",
    [0x0800] = "PIN"
}

credential_source_details = {
    [0x0100] = "MIFARE - UNKNOWN",
    [0x0101] = "MIFARE - CLASSIC_1K",
    [0x0102] = "MIFARE - CLASSIC_4K",
    [0x0103] = "MIFARE - ULTRALIGHT",
    [0x0104] = "MIFARE - DESFIRE",
    [0x0105] = "MIFARE - PLUS",
    
    [0x0200] = "ICLASS - UNKNOWN (no further details)",
    
    [0x0300] = "LOW_FREQUENCY - UNKNOWN (no further details)",
    
    [0x0400] = "RESERVED",
    
    [0x0500] = "PICOPASS - UNKNOWN (no further details)",
    
    [0x0600] = "LEGIC - UNKNOWN",
    [0x0601] = "LEGIC - PRIME",
    [0x0602] = "LEGIC - ISO15693",
    [0x0603] = "LEGIC - ISO14443A",
    [0x0604] = "LEGIC - ISO14443B",
    [0x0605] = "LEGIC - INSIDE Secure",
    [0x0606] = "LEGIC - FeliCa subset",
    
    [0x0700] = "SIO - UNKNOWN",
    [0x0701] = "SIO - ISO14443A-SEOS",
    [0x0702] = "SIO - ISO14443B-SEOS",
    [0x0703] = "SIO - MIFARE CLASSIC-SE",
    [0x0704] = "SIO - MIFARE DESFIRE-SE"
}

credential_source_details_sio_additional = {
    [0] = "UNKNOWN",
    [1] = "ISO14443A-SEOS",
    [2] = "ISO14443B-SEOS",
    [3] = "MIFARE CLASSIC-SE",
    [4] = "MIFARE DESFIRE-SE"
}

door_side = {
    [0] = "UNKNOWN",
    [1] = "INSIDE",
    [2] = "OUTSIDE",
    [3] = "BOTH"
}

execute_mode = {
    [0] = "ASAP",
    [1] = "IMMEDIATE",
    [2] = "DOORSEQUENCE"
}

door_mode = {
    [0] = "UNKNOWN",
    [1] = "FREE_OPEN",
    [2] = "FREE_UNLOCKED",
    [3] = "INSIDEOUT_LOCKED",
    [4] = "INSIDEOUT_SECURED",
    [5] = "RESTRICTED_LOCKED",
    [6] = "RESTRICTED_SECURED"
}

timing_mode = {
    [0] = "DEFAULT",
    [1] = "TIME",
    [2] = "INFINITE"
}

status = {
    [0] = "UNKNOWN",
    [1] = "OK",
    [2] = "OK_PENDING",
    [3] = "FAIL",
    [4] = "UNAVAILABLE",
    [5] = "FAIL_TYPE_ERROR",
    [6] = "FAIL_ENUM_ERROR"
}

indication = {
    [0] = "KEYPAD",
    [1] = "USE_TAG",
    [2] = "RESERVED1",
    [3] = "GRANTED",
    [4] = "DENIED",
    [5] = "BACKLIGHT",
    [6] = "ARMED",
    [7] = "DISARMED",
    [8] = "APPL1",
    [9] = "APPL2",
    [10] = "APPL3",
    [11] = "DOOR_HELD",
    [12] = "DOOR_FORCED",
    [13] = "USER_ATTENTION",
    [14] = "FIRE_ALARM",
    [15] = "BEACON_SIGNAL",
    [16] = "BURGLARY_ALARM",
    [17] = "PANIC_ALARM",
    [18] = "ACKNOWLEDGE"
}

physical_representation = {
    [0] = "AUTOMATIC",
    [1] = "VISUAL",
    [2] = "AUDIBLE",
    [3] = "TACTILE"
}

dtc_selection = {
    [0] = "ALL" ,
    [1] = "HAS_FAILED" ,
    [2] = "NOT_COMPLETED" ,
    [3] = "IS_FAILING" 
}

alarm_id = {
    [0x00] = "UNKNOWN",
    [0x01] = "FIRE",
    [0x02] = "BURGLARY",
    [0x03] = "PANIC",
    [0x04] = "ALARM_ID_4_RESERVED",
    [0x05] = "ALARM_ID_5_RESERVED",
    [0x06] = "ALARM_ID_6_RESERVED",
    [0x07] = "ALARM_ID_7_RESERVED",
    [0x08] = "ALARM_ID_8_RESERVED",
    [0x09] = "ALARM_ID_9_RESERVED",
    [0x0A] = "ALARM_ID_10_RESERVED",
    [0x0B] = "ALARM_ID_11_RESERVED",
    [0x0C] = "ALARM_ID_12_RESERVED",
    [0x0D] = "ALARM_ID_13_RESERVED",
    [0x0E] = "ALARM_ID_14_RESERVED",
    [0x0F] = "ALARM_ID_15_RESERVED",
    [0x10] = "ALARM_ID_16_RESERVED",
    [0x11] = "ALARM_ID_17_RESERVED",
    [0x12] = "ALARM_ID_18_RESERVED",
    [0x13] = "ALARM_ID_19_RESERVED",
    [0x14] = "ALARM_ID_20_RESERVED",
    [0x15] = "ALARM_ID_21_RESERVED",
    [0x16] = "ALARM_ID_22_RESERVED",
    [0x17] = "ALARM_ID_23_RESERVED",
    [0x18] = "ALARM_ID_24_RESERVED",
    [0x19] = "ALARM_ID_25_RESERVED",
    [0x1A] = "ALARM_ID_26_RESERVED",
    [0x1B] = "ALARM_ID_27_RESERVED",
    [0x1C] = "ALARM_ID_28_RESERVED",
    [0x1D] = "ALARM_ID_29_RESERVED",
    [0x1E] = "ALARM_ID_30_RESERVED",
    [0x1F] = "ALARM_ID_31_RESERVED",
    [0x20] = "ALARM_ID_32_RESERVED",
    [0x21] = "ALARM_ID_33_RESERVED",
    [0x22] = "ALARM_ID_34_RESERVED",
    [0x23] = "ALARM_ID_35_RESERVED",
    [0x24] = "ALARM_ID_36_RESERVED",
    [0x25] = "ALARM_ID_37_RESERVED",
    [0x26] = "ALARM_ID_38_RESERVED",
    [0x27] = "ALARM_ID_39_RESERVED",
    [0x28] = "ALARM_ID_40_RESERVED",
    [0x29] = "ALARM_ID_41_RESERVED",
    [0x2A] = "ALARM_ID_42_RESERVED",
    [0x2B] = "ALARM_ID_43_RESERVED",
    [0x2C] = "ALARM_ID_44_RESERVED",
    [0x2D] = "ALARM_ID_45_RESERVED",
    [0x2E] = "ALARM_ID_46_RESERVED",
    [0x2F] = "ALARM_ID_47_RESERVED",
    [0x30] = "ALARM_ID_48_RESERVED",
    [0x31] = "ALARM_ID_49_RESERVED",
    [0x32] = "ALARM_ID_50_RESERVED",
    [0x33] = "ALARM_ID_51_RESERVED",
    [0x34] = "ALARM_ID_52_RESERVED",
    [0x35] = "ALARM_ID_53_RESERVED",
    [0x36] = "ALARM_ID_54_RESERVED",
    [0x37] = "ALARM_ID_55_RESERVED",
    [0x38] = "ALARM_ID_56_RESERVED",
    [0x39] = "ALARM_ID_57_RESERVED",
    [0x3A] = "ALARM_ID_58_RESERVED",
    [0x3B] = "ALARM_ID_59_RESERVED",
    [0x3C] = "ALARM_ID_60_RESERVED",
    [0x3D] = "ALARM_ID_61_RESERVED",
    [0x3E] = "ALARM_ID_62_RESERVED",
    [0x3F] = "ALARM_ID_63_RESERVED",
    [0x40] = "ALARM_ID_64_APPL_SPECIFIC",
    [0x41] = "ALARM_ID_65_APPL_SPECIFIC",
    [0x42] = "ALARM_ID_66_APPL_SPECIFIC",
    [0x43] = "ALARM_ID_67_APPL_SPECIFIC",
    [0x44] = "ALARM_ID_68_APPL_SPECIFIC",
    [0x45] = "ALARM_ID_69_APPL_SPECIFIC",
    [0x46] = "ALARM_ID_70_APPL_SPECIFIC",
    [0x47] = "ALARM_ID_71_APPL_SPECIFIC",
    [0x48] = "ALARM_ID_72_APPL_SPECIFIC",
    [0x49] = "ALARM_ID_73_APPL_SPECIFIC",
    [0x4A] = "ALARM_ID_74_APPL_SPECIFIC",
    [0x4B] = "ALARM_ID_75_APPL_SPECIFIC",
    [0x4C] = "ALARM_ID_76_APPL_SPECIFIC",
    [0x4D] = "ALARM_ID_77_APPL_SPECIFIC",
    [0x4E] = "ALARM_ID_78_APPL_SPECIFIC",
    [0x4F] = "ALARM_ID_79_APPL_SPECIFIC",
    [0x50] = "ALARM_ID_80_APPL_SPECIFIC",
    [0x51] = "ALARM_ID_81_APPL_SPECIFIC",
    [0x52] = "ALARM_ID_82_APPL_SPECIFIC",
    [0x53] = "ALARM_ID_83_APPL_SPECIFIC",
    [0x54] = "ALARM_ID_84_APPL_SPECIFIC",
    [0x55] = "ALARM_ID_85_APPL_SPECIFIC",
    [0x56] = "ALARM_ID_86_APPL_SPECIFIC",
    [0x57] = "ALARM_ID_87_APPL_SPECIFIC",
    [0x58] = "ALARM_ID_88_APPL_SPECIFIC",
    [0x59] = "ALARM_ID_89_APPL_SPECIFIC",
    [0x5A] = "ALARM_ID_90_APPL_SPECIFIC",
    [0x5B] = "ALARM_ID_91_APPL_SPECIFIC",
    [0x5C] = "ALARM_ID_92_APPL_SPECIFIC",
    [0x5D] = "ALARM_ID_93_APPL_SPECIFIC",
    [0x5E] = "ALARM_ID_94_APPL_SPECIFIC",
    [0x5F] = "ALARM_ID_95_APPL_SPECIFIC",
    [0x60] = "ALARM_ID_96_APPL_SPECIFIC",
    [0x61] = "ALARM_ID_97_APPL_SPECIFIC",
    [0x62] = "ALARM_ID_98_APPL_SPECIFIC",
    [0x63] = "ALARM_ID_99_APPL_SPECIFIC",
    [0x64] = "ALARM_ID_100_APPL_SPECIFIC",
    [0x65] = "ALARM_ID_101_APPL_SPECIFIC",
    [0x66] = "ALARM_ID_102_APPL_SPECIFIC",
    [0x67] = "ALARM_ID_103_APPL_SPECIFIC",
    [0x68] = "ALARM_ID_104_APPL_SPECIFIC",
    [0x69] = "ALARM_ID_105_APPL_SPECIFIC",
    [0x6A] = "ALARM_ID_106_APPL_SPECIFIC",
    [0x6B] = "ALARM_ID_107_APPL_SPECIFIC",
    [0x6C] = "ALARM_ID_108_APPL_SPECIFIC",
    [0x6D] = "ALARM_ID_109_APPL_SPECIFIC",
    [0x6E] = "ALARM_ID_110_APPL_SPECIFIC",
    [0x6F] = "ALARM_ID_111_APPL_SPECIFIC",
    [0x70] = "ALARM_ID_112_APPL_SPECIFIC",
    [0x71] = "ALARM_ID_113_APPL_SPECIFIC",
    [0x72] = "ALARM_ID_114_APPL_SPECIFIC",
    [0x73] = "ALARM_ID_115_APPL_SPECIFIC",
    [0x74] = "ALARM_ID_116_APPL_SPECIFIC",
    [0x75] = "ALARM_ID_117_APPL_SPECIFIC",
    [0x76] = "ALARM_ID_118_APPL_SPECIFIC",
    [0x77] = "ALARM_ID_119_APPL_SPECIFIC",
    [0x78] = "ALARM_ID_120_APPL_SPECIFIC",
    [0x79] = "ALARM_ID_121_APPL_SPECIFIC",
    [0x7A] = "ALARM_ID_122_APPL_SPECIFIC",
    [0x7B] = "ALARM_ID_123_APPL_SPECIFIC",
    [0x7C] = "ALARM_ID_124_APPL_SPECIFIC",
    [0x7D] = "ALARM_ID_125_APPL_SPECIFIC",
    [0x7E] = "ALARM_ID_126_APPL_SPECIFIC",
    [0x7F] = "ALARM_ID_127_APPL_SPECIFIC",
    [0x80] = "ALARM_ID_128_APPL_SPECIFIC",
    [0x81] = "ALARM_ID_129_APPL_SPECIFIC",
    [0x82] = "ALARM_ID_130_APPL_SPECIFIC",
    [0x83] = "ALARM_ID_131_APPL_SPECIFIC",
    [0x84] = "ALARM_ID_132_APPL_SPECIFIC",
    [0x85] = "ALARM_ID_133_APPL_SPECIFIC",
    [0x86] = "ALARM_ID_134_APPL_SPECIFIC",
    [0x87] = "ALARM_ID_135_APPL_SPECIFIC",
    [0x88] = "ALARM_ID_136_APPL_SPECIFIC",
    [0x89] = "ALARM_ID_137_APPL_SPECIFIC",
    [0x8A] = "ALARM_ID_138_APPL_SPECIFIC",
    [0x8B] = "ALARM_ID_139_APPL_SPECIFIC",
    [0x8C] = "ALARM_ID_140_APPL_SPECIFIC",
    [0x8D] = "ALARM_ID_141_APPL_SPECIFIC",
    [0x8E] = "ALARM_ID_142_APPL_SPECIFIC",
    [0x8F] = "ALARM_ID_143_APPL_SPECIFIC",
    [0x90] = "ALARM_ID_144_APPL_SPECIFIC",
    [0x91] = "ALARM_ID_145_APPL_SPECIFIC",
    [0x92] = "ALARM_ID_146_APPL_SPECIFIC",
    [0x93] = "ALARM_ID_147_APPL_SPECIFIC",
    [0x94] = "ALARM_ID_148_APPL_SPECIFIC",
    [0x95] = "ALARM_ID_149_APPL_SPECIFIC",
    [0x96] = "ALARM_ID_150_APPL_SPECIFIC",
    [0x97] = "ALARM_ID_151_APPL_SPECIFIC",
    [0x98] = "ALARM_ID_152_APPL_SPECIFIC",
    [0x99] = "ALARM_ID_153_APPL_SPECIFIC",
    [0x9A] = "ALARM_ID_154_APPL_SPECIFIC",
    [0x9B] = "ALARM_ID_155_APPL_SPECIFIC",
    [0x9C] = "ALARM_ID_156_APPL_SPECIFIC",
    [0x9D] = "ALARM_ID_157_APPL_SPECIFIC",
    [0x9E] = "ALARM_ID_158_APPL_SPECIFIC",
    [0x9F] = "ALARM_ID_159_APPL_SPECIFIC",
    [0xA0] = "ALARM_ID_160_APPL_SPECIFIC",
    [0xA1] = "ALARM_ID_161_APPL_SPECIFIC",
    [0xA2] = "ALARM_ID_162_APPL_SPECIFIC",
    [0xA3] = "ALARM_ID_163_APPL_SPECIFIC",
    [0xA4] = "ALARM_ID_164_APPL_SPECIFIC",
    [0xA5] = "ALARM_ID_165_APPL_SPECIFIC",
    [0xA6] = "ALARM_ID_166_APPL_SPECIFIC",
    [0xA7] = "ALARM_ID_167_APPL_SPECIFIC",
    [0xA8] = "ALARM_ID_168_APPL_SPECIFIC",
    [0xA9] = "ALARM_ID_169_APPL_SPECIFIC",
    [0xAA] = "ALARM_ID_170_APPL_SPECIFIC",
    [0xAB] = "ALARM_ID_171_APPL_SPECIFIC",
    [0xAC] = "ALARM_ID_172_APPL_SPECIFIC",
    [0xAD] = "ALARM_ID_173_APPL_SPECIFIC",
    [0xAE] = "ALARM_ID_174_APPL_SPECIFIC",
    [0xAF] = "ALARM_ID_175_APPL_SPECIFIC",
    [0xB0] = "ALARM_ID_176_APPL_SPECIFIC",
    [0xB1] = "ALARM_ID_177_APPL_SPECIFIC",
    [0xB2] = "ALARM_ID_178_APPL_SPECIFIC",
    [0xB3] = "ALARM_ID_179_APPL_SPECIFIC",
    [0xB4] = "ALARM_ID_180_APPL_SPECIFIC",
    [0xB5] = "ALARM_ID_181_APPL_SPECIFIC",
    [0xB6] = "ALARM_ID_182_APPL_SPECIFIC",
    [0xB7] = "ALARM_ID_183_APPL_SPECIFIC",
    [0xB8] = "ALARM_ID_184_APPL_SPECIFIC",
    [0xB9] = "ALARM_ID_185_APPL_SPECIFIC",
    [0xBA] = "ALARM_ID_186_APPL_SPECIFIC",
    [0xBB] = "ALARM_ID_187_APPL_SPECIFIC",
    [0xBC] = "ALARM_ID_188_APPL_SPECIFIC",
    [0xBD] = "ALARM_ID_189_APPL_SPECIFIC",
    [0xBE] = "ALARM_ID_190_APPL_SPECIFIC",
    [0xBF] = "ALARM_ID_191_APPL_SPECIFIC",
    [0xC0] = "ALARM_ID_192_APPL_SPECIFIC",
    [0xC1] = "ALARM_ID_193_APPL_SPECIFIC",
    [0xC2] = "ALARM_ID_194_APPL_SPECIFIC",
    [0xC3] = "ALARM_ID_195_APPL_SPECIFIC",
    [0xC4] = "ALARM_ID_196_APPL_SPECIFIC",
    [0xC5] = "ALARM_ID_197_APPL_SPECIFIC",
    [0xC6] = "ALARM_ID_198_APPL_SPECIFIC",
    [0xC7] = "ALARM_ID_199_APPL_SPECIFIC",
    [0xC8] = "ALARM_ID_200_APPL_SPECIFIC",
    [0xC9] = "ALARM_ID_201_APPL_SPECIFIC",
    [0xCA] = "ALARM_ID_202_APPL_SPECIFIC",
    [0xCB] = "ALARM_ID_203_APPL_SPECIFIC",
    [0xCC] = "ALARM_ID_204_APPL_SPECIFIC",
    [0xCD] = "ALARM_ID_205_APPL_SPECIFIC",
    [0xCE] = "ALARM_ID_206_APPL_SPECIFIC",
    [0xCF] = "ALARM_ID_207_APPL_SPECIFIC",
    [0xD0] = "ALARM_ID_208_APPL_SPECIFIC",
    [0xD1] = "ALARM_ID_209_APPL_SPECIFIC",
    [0xD2] = "ALARM_ID_210_APPL_SPECIFIC",
    [0xD3] = "ALARM_ID_211_APPL_SPECIFIC",
    [0xD4] = "ALARM_ID_212_APPL_SPECIFIC",
    [0xD5] = "ALARM_ID_213_APPL_SPECIFIC",
    [0xD6] = "ALARM_ID_214_APPL_SPECIFIC",
    [0xD7] = "ALARM_ID_215_APPL_SPECIFIC",
    [0xD8] = "ALARM_ID_216_APPL_SPECIFIC",
    [0xD9] = "ALARM_ID_217_APPL_SPECIFIC",
    [0xDA] = "ALARM_ID_218_APPL_SPECIFIC",
    [0xDB] = "ALARM_ID_219_APPL_SPECIFIC",
    [0xDC] = "ALARM_ID_220_APPL_SPECIFIC",
    [0xDD] = "ALARM_ID_221_APPL_SPECIFIC",
    [0xDE] = "ALARM_ID_222_APPL_SPECIFIC",
    [0xDF] = "ALARM_ID_223_APPL_SPECIFIC",
    [0xE0] = "ALARM_ID_224_APPL_SPECIFIC",
    [0xE1] = "ALARM_ID_225_APPL_SPECIFIC",
    [0xE2] = "ALARM_ID_226_APPL_SPECIFIC",
    [0xE3] = "ALARM_ID_227_APPL_SPECIFIC",
    [0xE4] = "ALARM_ID_228_APPL_SPECIFIC",
    [0xE5] = "ALARM_ID_229_APPL_SPECIFIC",
    [0xE6] = "ALARM_ID_230_APPL_SPECIFIC",
    [0xE7] = "ALARM_ID_231_APPL_SPECIFIC",
    [0xE8] = "ALARM_ID_232_APPL_SPECIFIC",
    [0xE9] = "ALARM_ID_233_APPL_SPECIFIC",
    [0xEA] = "ALARM_ID_234_APPL_SPECIFIC",
    [0xEB] = "ALARM_ID_235_APPL_SPECIFIC",
    [0xEC] = "ALARM_ID_236_APPL_SPECIFIC",
    [0xED] = "ALARM_ID_237_APPL_SPECIFIC",
    [0xEE] = "ALARM_ID_238_APPL_SPECIFIC",
    [0xEF] = "ALARM_ID_239_APPL_SPECIFIC",
    [0xF0] = "ALARM_ID_240_APPL_SPECIFIC",
    [0xF1] = "ALARM_ID_241_APPL_SPECIFIC",
    [0xF2] = "ALARM_ID_242_APPL_SPECIFIC",
    [0xF3] = "ALARM_ID_243_APPL_SPECIFIC",
    [0xF4] = "ALARM_ID_244_APPL_SPECIFIC",
    [0xF5] = "ALARM_ID_245_APPL_SPECIFIC",
    [0xF6] = "ALARM_ID_246_APPL_SPECIFIC",
    [0xF7] = "ALARM_ID_247_APPL_SPECIFIC",
    [0xF8] = "ALARM_ID_248_APPL_SPECIFIC",
    [0xF9] = "ALARM_ID_249_APPL_SPECIFIC",
    [0xFA] = "ALARM_ID_250_APPL_SPECIFIC",
    [0xFB] = "ALARM_ID_251_APPL_SPECIFIC",
    [0xFC] = "ALARM_ID_252_APPL_SPECIFIC",
    [0xFD] = "ALARM_ID_253_APPL_SPECIFIC",
    [0xFE] = "ALARM_ID_254_APPL_SPECIFIC",
    [0xFF] = "ALARM_ID_255_APPL_SPECIFIC"
}

alarm_details = {
    [0x00] = "ALARM_DETAILS_0_APPL_SPECIFIC",
    [0x01] = "ALARM_DETAILS_1_APPL_SPECIFIC",
    [0x02] = "ALARM_DETAILS_2_APPL_SPECIFIC",
    [0x03] = "ALARM_DETAILS_3_APPL_SPECIFIC",
    [0x04] = "ALARM_DETAILS_4_APPL_SPECIFIC",
    [0x05] = "ALARM_DETAILS_5_APPL_SPECIFIC",
    [0x06] = "ALARM_DETAILS_6_APPL_SPECIFIC",
    [0x07] = "ALARM_DETAILS_7_APPL_SPECIFIC",
    [0x08] = "ALARM_DETAILS_8_APPL_SPECIFIC",
    [0x09] = "ALARM_DETAILS_9_APPL_SPECIFIC",
    [0x0A] = "ALARM_DETAILS_10_APPL_SPECIFIC",
    [0x0B] = "ALARM_DETAILS_11_APPL_SPECIFIC",
    [0x0C] = "ALARM_DETAILS_12_APPL_SPECIFIC",
    [0x0D] = "ALARM_DETAILS_13_APPL_SPECIFIC",
    [0x0E] = "ALARM_DETAILS_14_APPL_SPECIFIC",
    [0x0F] = "ALARM_DETAILS_15_APPL_SPECIFIC",
    [0x10] = "ALARM_DETAILS_16_APPL_SPECIFIC",
    [0x11] = "ALARM_DETAILS_17_APPL_SPECIFIC",
    [0x12] = "ALARM_DETAILS_18_APPL_SPECIFIC",
    [0x13] = "ALARM_DETAILS_19_APPL_SPECIFIC",
    [0x14] = "ALARM_DETAILS_20_APPL_SPECIFIC",
    [0x15] = "ALARM_DETAILS_21_APPL_SPECIFIC",
    [0x16] = "ALARM_DETAILS_22_APPL_SPECIFIC",
    [0x17] = "ALARM_DETAILS_23_APPL_SPECIFIC",
    [0x18] = "ALARM_DETAILS_24_APPL_SPECIFIC",
    [0x19] = "ALARM_DETAILS_25_APPL_SPECIFIC",
    [0x1A] = "ALARM_DETAILS_26_APPL_SPECIFIC",
    [0x1B] = "ALARM_DETAILS_27_APPL_SPECIFIC",
    [0x1C] = "ALARM_DETAILS_28_APPL_SPECIFIC",
    [0x1D] = "ALARM_DETAILS_29_APPL_SPECIFIC",
    [0x1E] = "ALARM_DETAILS_30_APPL_SPECIFIC",
    [0x1F] = "ALARM_DETAILS_31_APPL_SPECIFIC",
    [0x20] = "ALARM_DETAILS_32_APPL_SPECIFIC",
    [0x21] = "ALARM_DETAILS_33_APPL_SPECIFIC",
    [0x22] = "ALARM_DETAILS_34_APPL_SPECIFIC",
    [0x23] = "ALARM_DETAILS_35_APPL_SPECIFIC",
    [0x24] = "ALARM_DETAILS_36_APPL_SPECIFIC",
    [0x25] = "ALARM_DETAILS_37_APPL_SPECIFIC",
    [0x26] = "ALARM_DETAILS_38_APPL_SPECIFIC",
    [0x27] = "ALARM_DETAILS_39_APPL_SPECIFIC",
    [0x28] = "ALARM_DETAILS_40_APPL_SPECIFIC",
    [0x29] = "ALARM_DETAILS_41_APPL_SPECIFIC",
    [0x2A] = "ALARM_DETAILS_42_APPL_SPECIFIC",
    [0x2B] = "ALARM_DETAILS_43_APPL_SPECIFIC",
    [0x2C] = "ALARM_DETAILS_44_APPL_SPECIFIC",
    [0x2D] = "ALARM_DETAILS_45_APPL_SPECIFIC",
    [0x2E] = "ALARM_DETAILS_46_APPL_SPECIFIC",
    [0x2F] = "ALARM_DETAILS_47_APPL_SPECIFIC",
    [0x30] = "ALARM_DETAILS_48_APPL_SPECIFIC",
    [0x31] = "ALARM_DETAILS_49_APPL_SPECIFIC",
    [0x32] = "ALARM_DETAILS_50_APPL_SPECIFIC",
    [0x33] = "ALARM_DETAILS_51_APPL_SPECIFIC",
    [0x34] = "ALARM_DETAILS_52_APPL_SPECIFIC",
    [0x35] = "ALARM_DETAILS_53_APPL_SPECIFIC",
    [0x36] = "ALARM_DETAILS_54_APPL_SPECIFIC",
    [0x37] = "ALARM_DETAILS_55_APPL_SPECIFIC",
    [0x38] = "ALARM_DETAILS_56_APPL_SPECIFIC",
    [0x39] = "ALARM_DETAILS_57_APPL_SPECIFIC",
    [0x3A] = "ALARM_DETAILS_58_APPL_SPECIFIC",
    [0x3B] = "ALARM_DETAILS_59_APPL_SPECIFIC",
    [0x3C] = "ALARM_DETAILS_60_APPL_SPECIFIC",
    [0x3D] = "ALARM_DETAILS_61_APPL_SPECIFIC",
    [0x3E] = "ALARM_DETAILS_62_APPL_SPECIFIC",
    [0x3F] = "ALARM_DETAILS_63_APPL_SPECIFIC",
    [0x40] = "ALARM_DETAILS_64_APPL_SPECIFIC",
    [0x41] = "ALARM_DETAILS_65_APPL_SPECIFIC",
    [0x42] = "ALARM_DETAILS_66_APPL_SPECIFIC",
    [0x43] = "ALARM_DETAILS_67_APPL_SPECIFIC",
    [0x44] = "ALARM_DETAILS_68_APPL_SPECIFIC",
    [0x45] = "ALARM_DETAILS_69_APPL_SPECIFIC",
    [0x46] = "ALARM_DETAILS_70_APPL_SPECIFIC",
    [0x47] = "ALARM_DETAILS_71_APPL_SPECIFIC",
    [0x48] = "ALARM_DETAILS_72_APPL_SPECIFIC",
    [0x49] = "ALARM_DETAILS_73_APPL_SPECIFIC",
    [0x4A] = "ALARM_DETAILS_74_APPL_SPECIFIC",
    [0x4B] = "ALARM_DETAILS_75_APPL_SPECIFIC",
    [0x4C] = "ALARM_DETAILS_76_APPL_SPECIFIC",
    [0x4D] = "ALARM_DETAILS_77_APPL_SPECIFIC",
    [0x4E] = "ALARM_DETAILS_78_APPL_SPECIFIC",
    [0x4F] = "ALARM_DETAILS_79_APPL_SPECIFIC",
    [0x50] = "ALARM_DETAILS_80_APPL_SPECIFIC",
    [0x51] = "ALARM_DETAILS_81_APPL_SPECIFIC",
    [0x52] = "ALARM_DETAILS_82_APPL_SPECIFIC",
    [0x53] = "ALARM_DETAILS_83_APPL_SPECIFIC",
    [0x54] = "ALARM_DETAILS_84_APPL_SPECIFIC",
    [0x55] = "ALARM_DETAILS_85_APPL_SPECIFIC",
    [0x56] = "ALARM_DETAILS_86_APPL_SPECIFIC",
    [0x57] = "ALARM_DETAILS_87_APPL_SPECIFIC",
    [0x58] = "ALARM_DETAILS_88_APPL_SPECIFIC",
    [0x59] = "ALARM_DETAILS_89_APPL_SPECIFIC",
    [0x5A] = "ALARM_DETAILS_90_APPL_SPECIFIC",
    [0x5B] = "ALARM_DETAILS_91_APPL_SPECIFIC",
    [0x5C] = "ALARM_DETAILS_92_APPL_SPECIFIC",
    [0x5D] = "ALARM_DETAILS_93_APPL_SPECIFIC",
    [0x5E] = "ALARM_DETAILS_94_APPL_SPECIFIC",
    [0x5F] = "ALARM_DETAILS_95_APPL_SPECIFIC",
    [0x60] = "ALARM_DETAILS_96_APPL_SPECIFIC",
    [0x61] = "ALARM_DETAILS_97_APPL_SPECIFIC",
    [0x62] = "ALARM_DETAILS_98_APPL_SPECIFIC",
    [0x63] = "ALARM_DETAILS_99_APPL_SPECIFIC",
    [0x64] = "ALARM_DETAILS_100_APPL_SPECIFIC",
    [0x65] = "ALARM_DETAILS_101_APPL_SPECIFIC",
    [0x66] = "ALARM_DETAILS_102_APPL_SPECIFIC",
    [0x67] = "ALARM_DETAILS_103_APPL_SPECIFIC",
    [0x68] = "ALARM_DETAILS_104_APPL_SPECIFIC",
    [0x69] = "ALARM_DETAILS_105_APPL_SPECIFIC",
    [0x6A] = "ALARM_DETAILS_106_APPL_SPECIFIC",
    [0x6B] = "ALARM_DETAILS_107_APPL_SPECIFIC",
    [0x6C] = "ALARM_DETAILS_108_APPL_SPECIFIC",
    [0x6D] = "ALARM_DETAILS_109_APPL_SPECIFIC",
    [0x6E] = "ALARM_DETAILS_110_APPL_SPECIFIC",
    [0x6F] = "ALARM_DETAILS_111_APPL_SPECIFIC",
    [0x70] = "ALARM_DETAILS_112_APPL_SPECIFIC",
    [0x71] = "ALARM_DETAILS_113_APPL_SPECIFIC",
    [0x72] = "ALARM_DETAILS_114_APPL_SPECIFIC",
    [0x73] = "ALARM_DETAILS_115_APPL_SPECIFIC",
    [0x74] = "ALARM_DETAILS_116_APPL_SPECIFIC",
    [0x75] = "ALARM_DETAILS_117_APPL_SPECIFIC",
    [0x76] = "ALARM_DETAILS_118_APPL_SPECIFIC",
    [0x77] = "ALARM_DETAILS_119_APPL_SPECIFIC",
    [0x78] = "ALARM_DETAILS_120_APPL_SPECIFIC",
    [0x79] = "ALARM_DETAILS_121_APPL_SPECIFIC",
    [0x7A] = "ALARM_DETAILS_122_APPL_SPECIFIC",
    [0x7B] = "ALARM_DETAILS_123_APPL_SPECIFIC",
    [0x7C] = "ALARM_DETAILS_124_APPL_SPECIFIC",
    [0x7D] = "ALARM_DETAILS_125_APPL_SPECIFIC",
    [0x7E] = "ALARM_DETAILS_126_APPL_SPECIFIC",
    [0x7F] = "ALARM_DETAILS_127_APPL_SPECIFIC",
    [0x80] = "ALARM_DETAILS_128_APPL_SPECIFIC",
    [0x81] = "ALARM_DETAILS_129_APPL_SPECIFIC",
    [0x82] = "ALARM_DETAILS_130_APPL_SPECIFIC",
    [0x83] = "ALARM_DETAILS_131_APPL_SPECIFIC",
    [0x84] = "ALARM_DETAILS_132_APPL_SPECIFIC",
    [0x85] = "ALARM_DETAILS_133_APPL_SPECIFIC",
    [0x86] = "ALARM_DETAILS_134_APPL_SPECIFIC",
    [0x87] = "ALARM_DETAILS_135_APPL_SPECIFIC",
    [0x88] = "ALARM_DETAILS_136_APPL_SPECIFIC",
    [0x89] = "ALARM_DETAILS_137_APPL_SPECIFIC",
    [0x8A] = "ALARM_DETAILS_138_APPL_SPECIFIC",
    [0x8B] = "ALARM_DETAILS_139_APPL_SPECIFIC",
    [0x8C] = "ALARM_DETAILS_140_APPL_SPECIFIC",
    [0x8D] = "ALARM_DETAILS_141_APPL_SPECIFIC",
    [0x8E] = "ALARM_DETAILS_142_APPL_SPECIFIC",
    [0x8F] = "ALARM_DETAILS_143_APPL_SPECIFIC",
    [0x90] = "ALARM_DETAILS_144_APPL_SPECIFIC",
    [0x91] = "ALARM_DETAILS_145_APPL_SPECIFIC",
    [0x92] = "ALARM_DETAILS_146_APPL_SPECIFIC",
    [0x93] = "ALARM_DETAILS_147_APPL_SPECIFIC",
    [0x94] = "ALARM_DETAILS_148_APPL_SPECIFIC",
    [0x95] = "ALARM_DETAILS_149_APPL_SPECIFIC",
    [0x96] = "ALARM_DETAILS_150_APPL_SPECIFIC",
    [0x97] = "ALARM_DETAILS_151_APPL_SPECIFIC",
    [0x98] = "ALARM_DETAILS_152_APPL_SPECIFIC",
    [0x99] = "ALARM_DETAILS_153_APPL_SPECIFIC",
    [0x9A] = "ALARM_DETAILS_154_APPL_SPECIFIC",
    [0x9B] = "ALARM_DETAILS_155_APPL_SPECIFIC",
    [0x9C] = "ALARM_DETAILS_156_APPL_SPECIFIC",
    [0x9D] = "ALARM_DETAILS_157_APPL_SPECIFIC",
    [0x9E] = "ALARM_DETAILS_158_APPL_SPECIFIC",
    [0x9F] = "ALARM_DETAILS_159_APPL_SPECIFIC",
    [0xA0] = "ALARM_DETAILS_160_APPL_SPECIFIC",
    [0xA1] = "ALARM_DETAILS_161_APPL_SPECIFIC",
    [0xA2] = "ALARM_DETAILS_162_APPL_SPECIFIC",
    [0xA3] = "ALARM_DETAILS_163_APPL_SPECIFIC",
    [0xA4] = "ALARM_DETAILS_164_APPL_SPECIFIC",
    [0xA5] = "ALARM_DETAILS_165_APPL_SPECIFIC",
    [0xA6] = "ALARM_DETAILS_166_APPL_SPECIFIC",
    [0xA7] = "ALARM_DETAILS_167_APPL_SPECIFIC",
    [0xA8] = "ALARM_DETAILS_168_APPL_SPECIFIC",
    [0xA9] = "ALARM_DETAILS_169_APPL_SPECIFIC",
    [0xAA] = "ALARM_DETAILS_170_APPL_SPECIFIC",
    [0xAB] = "ALARM_DETAILS_171_APPL_SPECIFIC",
    [0xAC] = "ALARM_DETAILS_172_APPL_SPECIFIC",
    [0xAD] = "ALARM_DETAILS_173_APPL_SPECIFIC",
    [0xAE] = "ALARM_DETAILS_174_APPL_SPECIFIC",
    [0xAF] = "ALARM_DETAILS_175_APPL_SPECIFIC",
    [0xB0] = "ALARM_DETAILS_176_APPL_SPECIFIC",
    [0xB1] = "ALARM_DETAILS_177_APPL_SPECIFIC",
    [0xB2] = "ALARM_DETAILS_178_APPL_SPECIFIC",
    [0xB3] = "ALARM_DETAILS_179_APPL_SPECIFIC",
    [0xB4] = "ALARM_DETAILS_180_APPL_SPECIFIC",
    [0xB5] = "ALARM_DETAILS_181_APPL_SPECIFIC",
    [0xB6] = "ALARM_DETAILS_182_APPL_SPECIFIC",
    [0xB7] = "ALARM_DETAILS_183_APPL_SPECIFIC",
    [0xB8] = "ALARM_DETAILS_184_APPL_SPECIFIC",
    [0xB9] = "ALARM_DETAILS_185_APPL_SPECIFIC",
    [0xBA] = "ALARM_DETAILS_186_APPL_SPECIFIC",
    [0xBB] = "ALARM_DETAILS_187_APPL_SPECIFIC",
    [0xBC] = "ALARM_DETAILS_188_APPL_SPECIFIC",
    [0xBD] = "ALARM_DETAILS_189_APPL_SPECIFIC",
    [0xBE] = "ALARM_DETAILS_190_APPL_SPECIFIC",
    [0xBF] = "ALARM_DETAILS_191_APPL_SPECIFIC",
    [0xC0] = "ALARM_DETAILS_192_APPL_SPECIFIC",
    [0xC1] = "ALARM_DETAILS_193_APPL_SPECIFIC",
    [0xC2] = "ALARM_DETAILS_194_APPL_SPECIFIC",
    [0xC3] = "ALARM_DETAILS_195_APPL_SPECIFIC",
    [0xC4] = "ALARM_DETAILS_196_APPL_SPECIFIC",
    [0xC5] = "ALARM_DETAILS_197_APPL_SPECIFIC",
    [0xC6] = "ALARM_DETAILS_198_APPL_SPECIFIC",
    [0xC7] = "ALARM_DETAILS_199_APPL_SPECIFIC",
    [0xC8] = "ALARM_DETAILS_200_APPL_SPECIFIC",
    [0xC9] = "ALARM_DETAILS_201_APPL_SPECIFIC",
    [0xCA] = "ALARM_DETAILS_202_APPL_SPECIFIC",
    [0xCB] = "ALARM_DETAILS_203_APPL_SPECIFIC",
    [0xCC] = "ALARM_DETAILS_204_APPL_SPECIFIC",
    [0xCD] = "ALARM_DETAILS_205_APPL_SPECIFIC",
    [0xCE] = "ALARM_DETAILS_206_APPL_SPECIFIC",
    [0xCF] = "ALARM_DETAILS_207_APPL_SPECIFIC",
    [0xD0] = "ALARM_DETAILS_208_APPL_SPECIFIC",
    [0xD1] = "ALARM_DETAILS_209_APPL_SPECIFIC",
    [0xD2] = "ALARM_DETAILS_210_APPL_SPECIFIC",
    [0xD3] = "ALARM_DETAILS_211_APPL_SPECIFIC",
    [0xD4] = "ALARM_DETAILS_212_APPL_SPECIFIC",
    [0xD5] = "ALARM_DETAILS_213_APPL_SPECIFIC",
    [0xD6] = "ALARM_DETAILS_214_APPL_SPECIFIC",
    [0xD7] = "ALARM_DETAILS_215_APPL_SPECIFIC",
    [0xD8] = "ALARM_DETAILS_216_APPL_SPECIFIC",
    [0xD9] = "ALARM_DETAILS_217_APPL_SPECIFIC",
    [0xDA] = "ALARM_DETAILS_218_APPL_SPECIFIC",
    [0xDB] = "ALARM_DETAILS_219_APPL_SPECIFIC",
    [0xDC] = "ALARM_DETAILS_220_APPL_SPECIFIC",
    [0xDD] = "ALARM_DETAILS_221_APPL_SPECIFIC",
    [0xDE] = "ALARM_DETAILS_222_APPL_SPECIFIC",
    [0xDF] = "ALARM_DETAILS_223_APPL_SPECIFIC",
    [0xE0] = "ALARM_DETAILS_224_APPL_SPECIFIC",
    [0xE1] = "ALARM_DETAILS_225_APPL_SPECIFIC",
    [0xE2] = "ALARM_DETAILS_226_APPL_SPECIFIC",
    [0xE3] = "ALARM_DETAILS_227_APPL_SPECIFIC",
    [0xE4] = "ALARM_DETAILS_228_APPL_SPECIFIC",
    [0xE5] = "ALARM_DETAILS_229_APPL_SPECIFIC",
    [0xE6] = "ALARM_DETAILS_230_APPL_SPECIFIC",
    [0xE7] = "ALARM_DETAILS_231_APPL_SPECIFIC",
    [0xE8] = "ALARM_DETAILS_232_APPL_SPECIFIC",
    [0xE9] = "ALARM_DETAILS_233_APPL_SPECIFIC",
    [0xEA] = "ALARM_DETAILS_234_APPL_SPECIFIC",
    [0xEB] = "ALARM_DETAILS_235_APPL_SPECIFIC",
    [0xEC] = "ALARM_DETAILS_236_APPL_SPECIFIC",
    [0xED] = "ALARM_DETAILS_237_APPL_SPECIFIC",
    [0xEE] = "ALARM_DETAILS_238_APPL_SPECIFIC",
    [0xEF] = "ALARM_DETAILS_239_APPL_SPECIFIC",
    [0xF0] = "ALARM_DETAILS_240_APPL_SPECIFIC",
    [0xF1] = "ALARM_DETAILS_241_APPL_SPECIFIC",
    [0xF2] = "ALARM_DETAILS_242_APPL_SPECIFIC",
    [0xF3] = "ALARM_DETAILS_243_APPL_SPECIFIC",
    [0xF4] = "ALARM_DETAILS_244_APPL_SPECIFIC",
    [0xF5] = "ALARM_DETAILS_245_APPL_SPECIFIC",
    [0xF6] = "ALARM_DETAILS_246_APPL_SPECIFIC",
    [0xF7] = "ALARM_DETAILS_247_APPL_SPECIFIC",
    [0xF8] = "ALARM_DETAILS_248_APPL_SPECIFIC",
    [0xF9] = "ALARM_DETAILS_249_APPL_SPECIFIC",
    [0xFA] = "ALARM_DETAILS_250_APPL_SPECIFIC",
    [0xFB] = "ALARM_DETAILS_251_APPL_SPECIFIC",
    [0xFC] = "ALARM_DETAILS_252_APPL_SPECIFIC",
    [0xFD] = "ALARM_DETAILS_253_APPL_SPECIFIC",
    [0xFE] = "ALARM_DETAILS_254_APPL_SPECIFIC",
    [0xFF] = "ALARM_DETAILS_255_APPL_SPECIFIC"
}

alarm_level = {
    [0x00] = "ALARM_LEVEL_0_APPL_SPECIFIC",
    [0x01] = "ALARM_LEVEL_1_APPL_SPECIFIC",
    [0x02] = "ALARM_LEVEL_2_APPL_SPECIFIC",
    [0x03] = "ALARM_LEVEL_3_APPL_SPECIFIC",
    [0x04] = "ALARM_LEVEL_4_APPL_SPECIFIC",
    [0x05] = "ALARM_LEVEL_5_APPL_SPECIFIC",
    [0x06] = "ALARM_LEVEL_6_APPL_SPECIFIC",
    [0x07] = "ALARM_LEVEL_7_APPL_SPECIFIC",
    [0x08] = "ALARM_LEVEL_8_APPL_SPECIFIC",
    [0x09] = "ALARM_LEVEL_9_APPL_SPECIFIC",
    [0x0A] = "ALARM_LEVEL_10_APPL_SPECIFIC",
    [0x0B] = "ALARM_LEVEL_11_APPL_SPECIFIC",
    [0x0C] = "ALARM_LEVEL_12_APPL_SPECIFIC",
    [0x0D] = "ALARM_LEVEL_13_APPL_SPECIFIC",
    [0x0E] = "ALARM_LEVEL_14_APPL_SPECIFIC",
    [0x0F] = "ALARM_LEVEL_15_APPL_SPECIFIC",
    [0x10] = "ALARM_LEVEL_16_APPL_SPECIFIC",
    [0x11] = "ALARM_LEVEL_17_APPL_SPECIFIC",
    [0x12] = "ALARM_LEVEL_18_APPL_SPECIFIC",
    [0x13] = "ALARM_LEVEL_19_APPL_SPECIFIC",
    [0x14] = "ALARM_LEVEL_20_APPL_SPECIFIC",
    [0x15] = "ALARM_LEVEL_21_APPL_SPECIFIC",
    [0x16] = "ALARM_LEVEL_22_APPL_SPECIFIC",
    [0x17] = "ALARM_LEVEL_23_APPL_SPECIFIC",
    [0x18] = "ALARM_LEVEL_24_APPL_SPECIFIC",
    [0x19] = "ALARM_LEVEL_25_APPL_SPECIFIC",
    [0x1A] = "ALARM_LEVEL_26_APPL_SPECIFIC",
    [0x1B] = "ALARM_LEVEL_27_APPL_SPECIFIC",
    [0x1C] = "ALARM_LEVEL_28_APPL_SPECIFIC",
    [0x1D] = "ALARM_LEVEL_29_APPL_SPECIFIC",
    [0x1E] = "ALARM_LEVEL_30_APPL_SPECIFIC",
    [0x1F] = "ALARM_LEVEL_31_APPL_SPECIFIC",
    [0x20] = "ALARM_LEVEL_32_APPL_SPECIFIC",
    [0x21] = "ALARM_LEVEL_33_APPL_SPECIFIC",
    [0x22] = "ALARM_LEVEL_34_APPL_SPECIFIC",
    [0x23] = "ALARM_LEVEL_35_APPL_SPECIFIC",
    [0x24] = "ALARM_LEVEL_36_APPL_SPECIFIC",
    [0x25] = "ALARM_LEVEL_37_APPL_SPECIFIC",
    [0x26] = "ALARM_LEVEL_38_APPL_SPECIFIC",
    [0x27] = "ALARM_LEVEL_39_APPL_SPECIFIC",
    [0x28] = "ALARM_LEVEL_40_APPL_SPECIFIC",
    [0x29] = "ALARM_LEVEL_41_APPL_SPECIFIC",
    [0x2A] = "ALARM_LEVEL_42_APPL_SPECIFIC",
    [0x2B] = "ALARM_LEVEL_43_APPL_SPECIFIC",
    [0x2C] = "ALARM_LEVEL_44_APPL_SPECIFIC",
    [0x2D] = "ALARM_LEVEL_45_APPL_SPECIFIC",
    [0x2E] = "ALARM_LEVEL_46_APPL_SPECIFIC",
    [0x2F] = "ALARM_LEVEL_47_APPL_SPECIFIC",
    [0x30] = "ALARM_LEVEL_48_APPL_SPECIFIC",
    [0x31] = "ALARM_LEVEL_49_APPL_SPECIFIC",
    [0x32] = "ALARM_LEVEL_50_APPL_SPECIFIC",
    [0x33] = "ALARM_LEVEL_51_APPL_SPECIFIC",
    [0x34] = "ALARM_LEVEL_52_APPL_SPECIFIC",
    [0x35] = "ALARM_LEVEL_53_APPL_SPECIFIC",
    [0x36] = "ALARM_LEVEL_54_APPL_SPECIFIC",
    [0x37] = "ALARM_LEVEL_55_APPL_SPECIFIC",
    [0x38] = "ALARM_LEVEL_56_APPL_SPECIFIC",
    [0x39] = "ALARM_LEVEL_57_APPL_SPECIFIC",
    [0x3A] = "ALARM_LEVEL_58_APPL_SPECIFIC",
    [0x3B] = "ALARM_LEVEL_59_APPL_SPECIFIC",
    [0x3C] = "ALARM_LEVEL_60_APPL_SPECIFIC",
    [0x3D] = "ALARM_LEVEL_61_APPL_SPECIFIC",
    [0x3E] = "ALARM_LEVEL_62_APPL_SPECIFIC",
    [0x3F] = "ALARM_LEVEL_63_APPL_SPECIFIC",
    [0x40] = "ALARM_LEVEL_64_APPL_SPECIFIC",
    [0x41] = "ALARM_LEVEL_65_APPL_SPECIFIC",
    [0x42] = "ALARM_LEVEL_66_APPL_SPECIFIC",
    [0x43] = "ALARM_LEVEL_67_APPL_SPECIFIC",
    [0x44] = "ALARM_LEVEL_68_APPL_SPECIFIC",
    [0x45] = "ALARM_LEVEL_69_APPL_SPECIFIC",
    [0x46] = "ALARM_LEVEL_70_APPL_SPECIFIC",
    [0x47] = "ALARM_LEVEL_71_APPL_SPECIFIC",
    [0x48] = "ALARM_LEVEL_72_APPL_SPECIFIC",
    [0x49] = "ALARM_LEVEL_73_APPL_SPECIFIC",
    [0x4A] = "ALARM_LEVEL_74_APPL_SPECIFIC",
    [0x4B] = "ALARM_LEVEL_75_APPL_SPECIFIC",
    [0x4C] = "ALARM_LEVEL_76_APPL_SPECIFIC",
    [0x4D] = "ALARM_LEVEL_77_APPL_SPECIFIC",
    [0x4E] = "ALARM_LEVEL_78_APPL_SPECIFIC",
    [0x4F] = "ALARM_LEVEL_79_APPL_SPECIFIC",
    [0x50] = "ALARM_LEVEL_80_APPL_SPECIFIC",
    [0x51] = "ALARM_LEVEL_81_APPL_SPECIFIC",
    [0x52] = "ALARM_LEVEL_82_APPL_SPECIFIC",
    [0x53] = "ALARM_LEVEL_83_APPL_SPECIFIC",
    [0x54] = "ALARM_LEVEL_84_APPL_SPECIFIC",
    [0x55] = "ALARM_LEVEL_85_APPL_SPECIFIC",
    [0x56] = "ALARM_LEVEL_86_APPL_SPECIFIC",
    [0x57] = "ALARM_LEVEL_87_APPL_SPECIFIC",
    [0x58] = "ALARM_LEVEL_88_APPL_SPECIFIC",
    [0x59] = "ALARM_LEVEL_89_APPL_SPECIFIC",
    [0x5A] = "ALARM_LEVEL_90_APPL_SPECIFIC",
    [0x5B] = "ALARM_LEVEL_91_APPL_SPECIFIC",
    [0x5C] = "ALARM_LEVEL_92_APPL_SPECIFIC",
    [0x5D] = "ALARM_LEVEL_93_APPL_SPECIFIC",
    [0x5E] = "ALARM_LEVEL_94_APPL_SPECIFIC",
    [0x5F] = "ALARM_LEVEL_95_APPL_SPECIFIC",
    [0x60] = "ALARM_LEVEL_96_APPL_SPECIFIC",
    [0x61] = "ALARM_LEVEL_97_APPL_SPECIFIC",
    [0x62] = "ALARM_LEVEL_98_APPL_SPECIFIC",
    [0x63] = "ALARM_LEVEL_99_APPL_SPECIFIC",
    [0x64] = "ALARM_LEVEL_100_APPL_SPECIFIC",
    [0x65] = "ALARM_LEVEL_101_APPL_SPECIFIC",
    [0x66] = "ALARM_LEVEL_102_APPL_SPECIFIC",
    [0x67] = "ALARM_LEVEL_103_APPL_SPECIFIC",
    [0x68] = "ALARM_LEVEL_104_APPL_SPECIFIC",
    [0x69] = "ALARM_LEVEL_105_APPL_SPECIFIC",
    [0x6A] = "ALARM_LEVEL_106_APPL_SPECIFIC",
    [0x6B] = "ALARM_LEVEL_107_APPL_SPECIFIC",
    [0x6C] = "ALARM_LEVEL_108_APPL_SPECIFIC",
    [0x6D] = "ALARM_LEVEL_109_APPL_SPECIFIC",
    [0x6E] = "ALARM_LEVEL_110_APPL_SPECIFIC",
    [0x6F] = "ALARM_LEVEL_111_APPL_SPECIFIC",
    [0x70] = "ALARM_LEVEL_112_APPL_SPECIFIC",
    [0x71] = "ALARM_LEVEL_113_APPL_SPECIFIC",
    [0x72] = "ALARM_LEVEL_114_APPL_SPECIFIC",
    [0x73] = "ALARM_LEVEL_115_APPL_SPECIFIC",
    [0x74] = "ALARM_LEVEL_116_APPL_SPECIFIC",
    [0x75] = "ALARM_LEVEL_117_APPL_SPECIFIC",
    [0x76] = "ALARM_LEVEL_118_APPL_SPECIFIC",
    [0x77] = "ALARM_LEVEL_119_APPL_SPECIFIC",
    [0x78] = "ALARM_LEVEL_120_APPL_SPECIFIC",
    [0x79] = "ALARM_LEVEL_121_APPL_SPECIFIC",
    [0x7A] = "ALARM_LEVEL_122_APPL_SPECIFIC",
    [0x7B] = "ALARM_LEVEL_123_APPL_SPECIFIC",
    [0x7C] = "ALARM_LEVEL_124_APPL_SPECIFIC",
    [0x7D] = "ALARM_LEVEL_125_APPL_SPECIFIC",
    [0x7E] = "ALARM_LEVEL_126_APPL_SPECIFIC",
    [0x7F] = "ALARM_LEVEL_127_APPL_SPECIFIC",
    [0x80] = "ALARM_LEVEL_128_APPL_SPECIFIC",
    [0x81] = "ALARM_LEVEL_129_APPL_SPECIFIC",
    [0x82] = "ALARM_LEVEL_130_APPL_SPECIFIC",
    [0x83] = "ALARM_LEVEL_131_APPL_SPECIFIC",
    [0x84] = "ALARM_LEVEL_132_APPL_SPECIFIC",
    [0x85] = "ALARM_LEVEL_133_APPL_SPECIFIC",
    [0x86] = "ALARM_LEVEL_134_APPL_SPECIFIC",
    [0x87] = "ALARM_LEVEL_135_APPL_SPECIFIC",
    [0x88] = "ALARM_LEVEL_136_APPL_SPECIFIC",
    [0x89] = "ALARM_LEVEL_137_APPL_SPECIFIC",
    [0x8A] = "ALARM_LEVEL_138_APPL_SPECIFIC",
    [0x8B] = "ALARM_LEVEL_139_APPL_SPECIFIC",
    [0x8C] = "ALARM_LEVEL_140_APPL_SPECIFIC",
    [0x8D] = "ALARM_LEVEL_141_APPL_SPECIFIC",
    [0x8E] = "ALARM_LEVEL_142_APPL_SPECIFIC",
    [0x8F] = "ALARM_LEVEL_143_APPL_SPECIFIC",
    [0x90] = "ALARM_LEVEL_144_APPL_SPECIFIC",
    [0x91] = "ALARM_LEVEL_145_APPL_SPECIFIC",
    [0x92] = "ALARM_LEVEL_146_APPL_SPECIFIC",
    [0x93] = "ALARM_LEVEL_147_APPL_SPECIFIC",
    [0x94] = "ALARM_LEVEL_148_APPL_SPECIFIC",
    [0x95] = "ALARM_LEVEL_149_APPL_SPECIFIC",
    [0x96] = "ALARM_LEVEL_150_APPL_SPECIFIC",
    [0x97] = "ALARM_LEVEL_151_APPL_SPECIFIC",
    [0x98] = "ALARM_LEVEL_152_APPL_SPECIFIC",
    [0x99] = "ALARM_LEVEL_153_APPL_SPECIFIC",
    [0x9A] = "ALARM_LEVEL_154_APPL_SPECIFIC",
    [0x9B] = "ALARM_LEVEL_155_APPL_SPECIFIC",
    [0x9C] = "ALARM_LEVEL_156_APPL_SPECIFIC",
    [0x9D] = "ALARM_LEVEL_157_APPL_SPECIFIC",
    [0x9E] = "ALARM_LEVEL_158_APPL_SPECIFIC",
    [0x9F] = "ALARM_LEVEL_159_APPL_SPECIFIC",
    [0xA0] = "ALARM_LEVEL_160_APPL_SPECIFIC",
    [0xA1] = "ALARM_LEVEL_161_APPL_SPECIFIC",
    [0xA2] = "ALARM_LEVEL_162_APPL_SPECIFIC",
    [0xA3] = "ALARM_LEVEL_163_APPL_SPECIFIC",
    [0xA4] = "ALARM_LEVEL_164_APPL_SPECIFIC",
    [0xA5] = "ALARM_LEVEL_165_APPL_SPECIFIC",
    [0xA6] = "ALARM_LEVEL_166_APPL_SPECIFIC",
    [0xA7] = "ALARM_LEVEL_167_APPL_SPECIFIC",
    [0xA8] = "ALARM_LEVEL_168_APPL_SPECIFIC",
    [0xA9] = "ALARM_LEVEL_169_APPL_SPECIFIC",
    [0xAA] = "ALARM_LEVEL_170_APPL_SPECIFIC",
    [0xAB] = "ALARM_LEVEL_171_APPL_SPECIFIC",
    [0xAC] = "ALARM_LEVEL_172_APPL_SPECIFIC",
    [0xAD] = "ALARM_LEVEL_173_APPL_SPECIFIC",
    [0xAE] = "ALARM_LEVEL_174_APPL_SPECIFIC",
    [0xAF] = "ALARM_LEVEL_175_APPL_SPECIFIC",
    [0xB0] = "ALARM_LEVEL_176_APPL_SPECIFIC",
    [0xB1] = "ALARM_LEVEL_177_APPL_SPECIFIC",
    [0xB2] = "ALARM_LEVEL_178_APPL_SPECIFIC",
    [0xB3] = "ALARM_LEVEL_179_APPL_SPECIFIC",
    [0xB4] = "ALARM_LEVEL_180_APPL_SPECIFIC",
    [0xB5] = "ALARM_LEVEL_181_APPL_SPECIFIC",
    [0xB6] = "ALARM_LEVEL_182_APPL_SPECIFIC",
    [0xB7] = "ALARM_LEVEL_183_APPL_SPECIFIC",
    [0xB8] = "ALARM_LEVEL_184_APPL_SPECIFIC",
    [0xB9] = "ALARM_LEVEL_185_APPL_SPECIFIC",
    [0xBA] = "ALARM_LEVEL_186_APPL_SPECIFIC",
    [0xBB] = "ALARM_LEVEL_187_APPL_SPECIFIC",
    [0xBC] = "ALARM_LEVEL_188_APPL_SPECIFIC",
    [0xBD] = "ALARM_LEVEL_189_APPL_SPECIFIC",
    [0xBE] = "ALARM_LEVEL_190_APPL_SPECIFIC",
    [0xBF] = "ALARM_LEVEL_191_APPL_SPECIFIC",
    [0xC0] = "ALARM_LEVEL_192_APPL_SPECIFIC",
    [0xC1] = "ALARM_LEVEL_193_APPL_SPECIFIC",
    [0xC2] = "ALARM_LEVEL_194_APPL_SPECIFIC",
    [0xC3] = "ALARM_LEVEL_195_APPL_SPECIFIC",
    [0xC4] = "ALARM_LEVEL_196_APPL_SPECIFIC",
    [0xC5] = "ALARM_LEVEL_197_APPL_SPECIFIC",
    [0xC6] = "ALARM_LEVEL_198_APPL_SPECIFIC",
    [0xC7] = "ALARM_LEVEL_199_APPL_SPECIFIC",
    [0xC8] = "ALARM_LEVEL_200_APPL_SPECIFIC",
    [0xC9] = "ALARM_LEVEL_201_APPL_SPECIFIC",
    [0xCA] = "ALARM_LEVEL_202_APPL_SPECIFIC",
    [0xCB] = "ALARM_LEVEL_203_APPL_SPECIFIC",
    [0xCC] = "ALARM_LEVEL_204_APPL_SPECIFIC",
    [0xCD] = "ALARM_LEVEL_205_APPL_SPECIFIC",
    [0xCE] = "ALARM_LEVEL_206_APPL_SPECIFIC",
    [0xCF] = "ALARM_LEVEL_207_APPL_SPECIFIC",
    [0xD0] = "ALARM_LEVEL_208_APPL_SPECIFIC",
    [0xD1] = "ALARM_LEVEL_209_APPL_SPECIFIC",
    [0xD2] = "ALARM_LEVEL_210_APPL_SPECIFIC",
    [0xD3] = "ALARM_LEVEL_211_APPL_SPECIFIC",
    [0xD4] = "ALARM_LEVEL_212_APPL_SPECIFIC",
    [0xD5] = "ALARM_LEVEL_213_APPL_SPECIFIC",
    [0xD6] = "ALARM_LEVEL_214_APPL_SPECIFIC",
    [0xD7] = "ALARM_LEVEL_215_APPL_SPECIFIC",
    [0xD8] = "ALARM_LEVEL_216_APPL_SPECIFIC",
    [0xD9] = "ALARM_LEVEL_217_APPL_SPECIFIC",
    [0xDA] = "ALARM_LEVEL_218_APPL_SPECIFIC",
    [0xDB] = "ALARM_LEVEL_219_APPL_SPECIFIC",
    [0xDC] = "ALARM_LEVEL_220_APPL_SPECIFIC",
    [0xDD] = "ALARM_LEVEL_221_APPL_SPECIFIC",
    [0xDE] = "ALARM_LEVEL_222_APPL_SPECIFIC",
    [0xDF] = "ALARM_LEVEL_223_APPL_SPECIFIC",
    [0xE0] = "ALARM_LEVEL_224_APPL_SPECIFIC",
    [0xE1] = "ALARM_LEVEL_225_APPL_SPECIFIC",
    [0xE2] = "ALARM_LEVEL_226_APPL_SPECIFIC",
    [0xE3] = "ALARM_LEVEL_227_APPL_SPECIFIC",
    [0xE4] = "ALARM_LEVEL_228_APPL_SPECIFIC",
    [0xE5] = "ALARM_LEVEL_229_APPL_SPECIFIC",
    [0xE6] = "ALARM_LEVEL_230_APPL_SPECIFIC",
    [0xE7] = "ALARM_LEVEL_231_APPL_SPECIFIC",
    [0xE8] = "ALARM_LEVEL_232_APPL_SPECIFIC",
    [0xE9] = "ALARM_LEVEL_233_APPL_SPECIFIC",
    [0xEA] = "ALARM_LEVEL_234_APPL_SPECIFIC",
    [0xEB] = "ALARM_LEVEL_235_APPL_SPECIFIC",
    [0xEC] = "ALARM_LEVEL_236_APPL_SPECIFIC",
    [0xED] = "ALARM_LEVEL_237_APPL_SPECIFIC",
    [0xEE] = "ALARM_LEVEL_238_APPL_SPECIFIC",
    [0xEF] = "ALARM_LEVEL_239_APPL_SPECIFIC",
    [0xF0] = "ALARM_LEVEL_240_APPL_SPECIFIC",
    [0xF1] = "ALARM_LEVEL_241_APPL_SPECIFIC",
    [0xF2] = "ALARM_LEVEL_242_APPL_SPECIFIC",
    [0xF3] = "ALARM_LEVEL_243_APPL_SPECIFIC",
    [0xF4] = "ALARM_LEVEL_244_APPL_SPECIFIC",
    [0xF5] = "ALARM_LEVEL_245_APPL_SPECIFIC",
    [0xF6] = "ALARM_LEVEL_246_APPL_SPECIFIC",
    [0xF7] = "ALARM_LEVEL_247_APPL_SPECIFIC",
    [0xF8] = "ALARM_LEVEL_248_APPL_SPECIFIC",
    [0xF9] = "ALARM_LEVEL_249_APPL_SPECIFIC",
    [0xFA] = "ALARM_LEVEL_250_APPL_SPECIFIC",
    [0xFB] = "ALARM_LEVEL_251_APPL_SPECIFIC",
    [0xFC] = "ALARM_LEVEL_252_APPL_SPECIFIC",
    [0xFD] = "ALARM_LEVEL_253_APPL_SPECIFIC",
    [0xFE] = "ALARM_LEVEL_254_APPL_SPECIFIC",
    [0xFF] = "ALARM_LEVEL_255_APPL_SPECIFIC"
} 

alarm_selection = {
    [0] = "NOT_ACTIVE_NOT_OCCURRED" ,
    [1] = "ACTIVE_NOT_OCCURRED" ,
    [2] = "NOT_ACTIVE_OCCURRED" ,
    [3] = "ACTIVE_OCCURRED" ,
    [255] = "ALL" 
}

config_notification = {
    [0] = "EXISTING" ,
    [1] = "ADDED" ,
    [2] = "REMOVED" ,
}

product_class = {
    [0x00] = "UNKNOWN",
    [0x01] = "ALARM_LIGHT",
    [0x02] = "DOOR_AUTOMATICS",
    [0x03] = "DOOR_AUTOMATICS_MOTION_SENSOR",
    [0x04] = "ELECTRIC_STRIKE",
    [0x05] = "GATEWAY",
    [0x06] = "IO_MODULE",
    [0x07] = "KEYPAD",
    [0x08] = "KEYPAD_READER",
    [0x09] = "LOCK",
    [0x0A] = "MAGNETIC_DOOR_HOLDER",
    [0x0B] = "MAGNETIC_LOCK",
    [0x0C] = "MOTION_SENSOR",
    [0x0D] = "MOTORLOCK_CONTROLLER",
    [0x0E] = "PANIC_BAR",
    [0x0F] = "PUSHBUTTON_DISABLED",
    [0x10] = "PUSHBUTTON_EMERGENCY",
    [0x11] = "PUSHBUTTON_EXIT",
    [0x12] = "READER",
    [0x13] = "SECURITY_LOCK",
    [0x14] = "HYBRID_LOCK",
    [0x15] = "ELMECH_LOCKING_INTERFACE",
    [0x16] = "ESCAPE_ROUTE"
}

parameter_type = {
    [0] = "UINTVAR", --note: uIntVar (unsigned int variable length) is called only "uint" in Lua/wireshark lingo. 
    [1] = "BOOLEAN",    -- 0xFF = true ; 0x00 = false
    [2] = "STRING",     -- [ length(uintvar) | characters ] max 128 UTF8 chars, i.e. length <= 512.
    [3] = "SHORT",      -- 2 bytes, signed, 0xFFFF = -1 ; 0x0001 = 1 ; range -32768 to 32767
    [4] = "RESERVED",   -- don't use
    [5] = "BYTE_ARRAY"  -- [ length(uintvar) |    bytes   ]
}

action_id = {
    [0x00] = "STOP",
    [0x01] = "ACTION_ID_1_RESERVED",
    [0x02] = "ACTION_ID_2_RESERVED",
    [0x03] = "ACTION_ID_3_RESERVED",
    [0x04] = "ACTION_ID_4_RESERVED",
    [0x05] = "ACTION_ID_5_RESERVED",
    [0x06] = "ACTION_ID_6_RESERVED",
    [0x07] = "ACTION_ID_7_RESERVED",
    [0x08] = "ACTION_ID_8_RESERVED",
    [0x09] = "ACTION_ID_9_RESERVED",
    [0x0A] = "ACTION_ID_10_RESERVED",
    [0x0B] = "ACTION_ID_11_RESERVED",
    [0x0C] = "ACTION_ID_12_RESERVED",
    [0x0D] = "ACTION_ID_13_RESERVED",
    [0x0E] = "ACTION_ID_14_RESERVED",
    [0x0F] = "ACTION_ID_15_RESERVED",
    [0x10] = "ACTION_ID_16_RESERVED",
    [0x11] = "ACTION_ID_17_RESERVED",
    [0x12] = "ACTION_ID_18_RESERVED",
    [0x13] = "ACTION_ID_19_RESERVED",
    [0x14] = "ACTION_ID_20_RESERVED",
    [0x15] = "ACTION_ID_21_RESERVED",
    [0x16] = "ACTION_ID_22_RESERVED",
    [0x17] = "ACTION_ID_23_RESERVED",
    [0x18] = "ACTION_ID_24_RESERVED",
    [0x19] = "ACTION_ID_25_RESERVED",
    [0x1A] = "ACTION_ID_26_RESERVED",
    [0x1B] = "ACTION_ID_27_RESERVED",
    [0x1C] = "ACTION_ID_28_RESERVED",
    [0x1D] = "ACTION_ID_29_RESERVED",
    [0x1E] = "ACTION_ID_30_RESERVED",
    [0x1F] = "ACTION_ID_31_RESERVED",
    [0x20] = "ACTION_ID_32_RESERVED",
    [0x21] = "ACTION_ID_33_RESERVED",
    [0x22] = "ACTION_ID_34_RESERVED",
    [0x23] = "ACTION_ID_35_RESERVED",
    [0x24] = "ACTION_ID_36_RESERVED",
    [0x25] = "ACTION_ID_37_RESERVED",
    [0x26] = "ACTION_ID_38_RESERVED",
    [0x27] = "ACTION_ID_39_RESERVED",
    [0x28] = "ACTION_ID_40_RESERVED",
    [0x29] = "ACTION_ID_41_RESERVED",
    [0x2A] = "ACTION_ID_42_RESERVED",
    [0x2B] = "ACTION_ID_43_RESERVED",
    [0x2C] = "ACTION_ID_44_RESERVED",
    [0x2D] = "ACTION_ID_45_RESERVED",
    [0x2E] = "ACTION_ID_46_RESERVED",
    [0x2F] = "ACTION_ID_47_RESERVED",
    [0x30] = "ACTION_ID_48_RESERVED",
    [0x31] = "ACTION_ID_49_RESERVED",
    [0x32] = "ACTION_ID_50_RESERVED",
    [0x33] = "ACTION_ID_51_RESERVED",
    [0x34] = "ACTION_ID_52_RESERVED",
    [0x35] = "ACTION_ID_53_RESERVED",
    [0x36] = "ACTION_ID_54_RESERVED",
    [0x37] = "ACTION_ID_55_RESERVED",
    [0x38] = "ACTION_ID_56_RESERVED",
    [0x39] = "ACTION_ID_57_RESERVED",
    [0x3A] = "ACTION_ID_58_RESERVED",
    [0x3B] = "ACTION_ID_59_RESERVED",
    [0x3C] = "ACTION_ID_60_RESERVED",
    [0x3D] = "ACTION_ID_61_RESERVED",
    [0x3E] = "ACTION_ID_62_RESERVED",
    [0x3F] = "ACTION_ID_63_RESERVED",
    [0x40] = "ACTION_ID_64_APPL_SPECIFIC",
    [0x41] = "ACTION_ID_65_APPL_SPECIFIC",
    [0x42] = "ACTION_ID_66_APPL_SPECIFIC",
    [0x43] = "ACTION_ID_67_APPL_SPECIFIC",
    [0x44] = "ACTION_ID_68_APPL_SPECIFIC",
    [0x45] = "ACTION_ID_69_APPL_SPECIFIC",
    [0x46] = "ACTION_ID_70_APPL_SPECIFIC",
    [0x47] = "ACTION_ID_71_APPL_SPECIFIC",
    [0x48] = "ACTION_ID_72_APPL_SPECIFIC",
    [0x49] = "ACTION_ID_73_APPL_SPECIFIC",
    [0x4A] = "ACTION_ID_74_APPL_SPECIFIC",
    [0x4B] = "ACTION_ID_75_APPL_SPECIFIC",
    [0x4C] = "ACTION_ID_76_APPL_SPECIFIC",
    [0x4D] = "ACTION_ID_77_APPL_SPECIFIC",
    [0x4E] = "ACTION_ID_78_APPL_SPECIFIC",
    [0x4F] = "ACTION_ID_79_APPL_SPECIFIC",
    [0x50] = "ACTION_ID_80_APPL_SPECIFIC",
    [0x51] = "ACTION_ID_81_APPL_SPECIFIC",
    [0x52] = "ACTION_ID_82_APPL_SPECIFIC",
    [0x53] = "ACTION_ID_83_APPL_SPECIFIC",
    [0x54] = "ACTION_ID_84_APPL_SPECIFIC",
    [0x55] = "ACTION_ID_85_APPL_SPECIFIC",
    [0x56] = "ACTION_ID_86_APPL_SPECIFIC",
    [0x57] = "ACTION_ID_87_APPL_SPECIFIC",
    [0x58] = "ACTION_ID_88_APPL_SPECIFIC",
    [0x59] = "ACTION_ID_89_APPL_SPECIFIC",
    [0x5A] = "ACTION_ID_90_APPL_SPECIFIC",
    [0x5B] = "ACTION_ID_91_APPL_SPECIFIC",
    [0x5C] = "ACTION_ID_92_APPL_SPECIFIC",
    [0x5D] = "ACTION_ID_93_APPL_SPECIFIC",
    [0x5E] = "ACTION_ID_94_APPL_SPECIFIC",
    [0x5F] = "ACTION_ID_95_APPL_SPECIFIC",
    [0x60] = "ACTION_ID_96_APPL_SPECIFIC",
    [0x61] = "ACTION_ID_97_APPL_SPECIFIC",
    [0x62] = "ACTION_ID_98_APPL_SPECIFIC",
    [0x63] = "ACTION_ID_99_APPL_SPECIFIC",
    [0x64] = "ACTION_ID_100_APPL_SPECIFIC",
    [0x65] = "ACTION_ID_101_APPL_SPECIFIC",
    [0x66] = "ACTION_ID_102_APPL_SPECIFIC",
    [0x67] = "ACTION_ID_103_APPL_SPECIFIC",
    [0x68] = "ACTION_ID_104_APPL_SPECIFIC",
    [0x69] = "ACTION_ID_105_APPL_SPECIFIC",
    [0x6A] = "ACTION_ID_106_APPL_SPECIFIC",
    [0x6B] = "ACTION_ID_107_APPL_SPECIFIC",
    [0x6C] = "ACTION_ID_108_APPL_SPECIFIC",
    [0x6D] = "ACTION_ID_109_APPL_SPECIFIC",
    [0x6E] = "ACTION_ID_110_APPL_SPECIFIC",
    [0x6F] = "ACTION_ID_111_APPL_SPECIFIC",
    [0x70] = "ACTION_ID_112_APPL_SPECIFIC",
    [0x71] = "ACTION_ID_113_APPL_SPECIFIC",
    [0x72] = "ACTION_ID_114_APPL_SPECIFIC",
    [0x73] = "ACTION_ID_115_APPL_SPECIFIC",
    [0x74] = "ACTION_ID_116_APPL_SPECIFIC",
    [0x75] = "ACTION_ID_117_APPL_SPECIFIC",
    [0x76] = "ACTION_ID_118_APPL_SPECIFIC",
    [0x77] = "ACTION_ID_119_APPL_SPECIFIC",
    [0x78] = "ACTION_ID_120_APPL_SPECIFIC",
    [0x79] = "ACTION_ID_121_APPL_SPECIFIC",
    [0x7A] = "ACTION_ID_122_APPL_SPECIFIC",
    [0x7B] = "ACTION_ID_123_APPL_SPECIFIC",
    [0x7C] = "ACTION_ID_124_APPL_SPECIFIC",
    [0x7D] = "ACTION_ID_125_APPL_SPECIFIC",
    [0x7E] = "ACTION_ID_126_APPL_SPECIFIC",
    [0x7F] = "ACTION_ID_127_APPL_SPECIFIC",
    [0x80] = "ACTION_ID_128_APPL_SPECIFIC",
    [0x81] = "ACTION_ID_129_APPL_SPECIFIC",
    [0x82] = "ACTION_ID_130_APPL_SPECIFIC",
    [0x83] = "ACTION_ID_131_APPL_SPECIFIC",
    [0x84] = "ACTION_ID_132_APPL_SPECIFIC",
    [0x85] = "ACTION_ID_133_APPL_SPECIFIC",
    [0x86] = "ACTION_ID_134_APPL_SPECIFIC",
    [0x87] = "ACTION_ID_135_APPL_SPECIFIC",
    [0x88] = "ACTION_ID_136_APPL_SPECIFIC",
    [0x89] = "ACTION_ID_137_APPL_SPECIFIC",
    [0x8A] = "ACTION_ID_138_APPL_SPECIFIC",
    [0x8B] = "ACTION_ID_139_APPL_SPECIFIC",
    [0x8C] = "ACTION_ID_140_APPL_SPECIFIC",
    [0x8D] = "ACTION_ID_141_APPL_SPECIFIC",
    [0x8E] = "ACTION_ID_142_APPL_SPECIFIC",
    [0x8F] = "ACTION_ID_143_APPL_SPECIFIC",
    [0x90] = "ACTION_ID_144_APPL_SPECIFIC",
    [0x91] = "ACTION_ID_145_APPL_SPECIFIC",
    [0x92] = "ACTION_ID_146_APPL_SPECIFIC",
    [0x93] = "ACTION_ID_147_APPL_SPECIFIC",
    [0x94] = "ACTION_ID_148_APPL_SPECIFIC",
    [0x95] = "ACTION_ID_149_APPL_SPECIFIC",
    [0x96] = "ACTION_ID_150_APPL_SPECIFIC",
    [0x97] = "ACTION_ID_151_APPL_SPECIFIC",
    [0x98] = "ACTION_ID_152_APPL_SPECIFIC",
    [0x99] = "ACTION_ID_153_APPL_SPECIFIC",
    [0x9A] = "ACTION_ID_154_APPL_SPECIFIC",
    [0x9B] = "ACTION_ID_155_APPL_SPECIFIC",
    [0x9C] = "ACTION_ID_156_APPL_SPECIFIC",
    [0x9D] = "ACTION_ID_157_APPL_SPECIFIC",
    [0x9E] = "ACTION_ID_158_APPL_SPECIFIC",
    [0x9F] = "ACTION_ID_159_APPL_SPECIFIC",
    [0xA0] = "ACTION_ID_160_APPL_SPECIFIC",
    [0xA1] = "ACTION_ID_161_APPL_SPECIFIC",
    [0xA2] = "ACTION_ID_162_APPL_SPECIFIC",
    [0xA3] = "ACTION_ID_163_APPL_SPECIFIC",
    [0xA4] = "ACTION_ID_164_APPL_SPECIFIC",
    [0xA5] = "ACTION_ID_165_APPL_SPECIFIC",
    [0xA6] = "ACTION_ID_166_APPL_SPECIFIC",
    [0xA7] = "ACTION_ID_167_APPL_SPECIFIC",
    [0xA8] = "ACTION_ID_168_APPL_SPECIFIC",
    [0xA9] = "ACTION_ID_169_APPL_SPECIFIC",
    [0xAA] = "ACTION_ID_170_APPL_SPECIFIC",
    [0xAB] = "ACTION_ID_171_APPL_SPECIFIC",
    [0xAC] = "ACTION_ID_172_APPL_SPECIFIC",
    [0xAD] = "ACTION_ID_173_APPL_SPECIFIC",
    [0xAE] = "ACTION_ID_174_APPL_SPECIFIC",
    [0xAF] = "ACTION_ID_175_APPL_SPECIFIC",
    [0xB0] = "ACTION_ID_176_APPL_SPECIFIC",
    [0xB1] = "ACTION_ID_177_APPL_SPECIFIC",
    [0xB2] = "ACTION_ID_178_APPL_SPECIFIC",
    [0xB3] = "ACTION_ID_179_APPL_SPECIFIC",
    [0xB4] = "ACTION_ID_180_APPL_SPECIFIC",
    [0xB5] = "ACTION_ID_181_APPL_SPECIFIC",
    [0xB6] = "ACTION_ID_182_APPL_SPECIFIC",
    [0xB7] = "ACTION_ID_183_APPL_SPECIFIC",
    [0xB8] = "ACTION_ID_184_APPL_SPECIFIC",
    [0xB9] = "ACTION_ID_185_APPL_SPECIFIC",
    [0xBA] = "ACTION_ID_186_APPL_SPECIFIC",
    [0xBB] = "ACTION_ID_187_APPL_SPECIFIC",
    [0xBC] = "ACTION_ID_188_APPL_SPECIFIC",
    [0xBD] = "ACTION_ID_189_APPL_SPECIFIC",
    [0xBE] = "ACTION_ID_190_APPL_SPECIFIC",
    [0xBF] = "ACTION_ID_191_APPL_SPECIFIC",
    [0xC0] = "ACTION_ID_192_APPL_SPECIFIC",
    [0xC1] = "ACTION_ID_193_APPL_SPECIFIC",
    [0xC2] = "ACTION_ID_194_APPL_SPECIFIC",
    [0xC3] = "ACTION_ID_195_APPL_SPECIFIC",
    [0xC4] = "ACTION_ID_196_APPL_SPECIFIC",
    [0xC5] = "ACTION_ID_197_APPL_SPECIFIC",
    [0xC6] = "ACTION_ID_198_APPL_SPECIFIC",
    [0xC7] = "ACTION_ID_199_APPL_SPECIFIC",
    [0xC8] = "ACTION_ID_200_APPL_SPECIFIC",
    [0xC9] = "ACTION_ID_201_APPL_SPECIFIC",
    [0xCA] = "ACTION_ID_202_APPL_SPECIFIC",
    [0xCB] = "ACTION_ID_203_APPL_SPECIFIC",
    [0xCC] = "ACTION_ID_204_APPL_SPECIFIC",
    [0xCD] = "ACTION_ID_205_APPL_SPECIFIC",
    [0xCE] = "ACTION_ID_206_APPL_SPECIFIC",
    [0xCF] = "ACTION_ID_207_APPL_SPECIFIC",
    [0xD0] = "ACTION_ID_208_APPL_SPECIFIC",
    [0xD1] = "ACTION_ID_209_APPL_SPECIFIC",
    [0xD2] = "ACTION_ID_210_APPL_SPECIFIC",
    [0xD3] = "ACTION_ID_211_APPL_SPECIFIC",
    [0xD4] = "ACTION_ID_212_APPL_SPECIFIC",
    [0xD5] = "ACTION_ID_213_APPL_SPECIFIC",
    [0xD6] = "ACTION_ID_214_APPL_SPECIFIC",
    [0xD7] = "ACTION_ID_215_APPL_SPECIFIC",
    [0xD8] = "ACTION_ID_216_APPL_SPECIFIC",
    [0xD9] = "ACTION_ID_217_APPL_SPECIFIC",
    [0xDA] = "ACTION_ID_218_APPL_SPECIFIC",
    [0xDB] = "ACTION_ID_219_APPL_SPECIFIC",
    [0xDC] = "ACTION_ID_220_APPL_SPECIFIC",
    [0xDD] = "ACTION_ID_221_APPL_SPECIFIC",
    [0xDE] = "ACTION_ID_222_APPL_SPECIFIC",
    [0xDF] = "ACTION_ID_223_APPL_SPECIFIC",
    [0xE0] = "ACTION_ID_224_APPL_SPECIFIC",
    [0xE1] = "ACTION_ID_225_APPL_SPECIFIC",
    [0xE2] = "ACTION_ID_226_APPL_SPECIFIC",
    [0xE3] = "ACTION_ID_227_APPL_SPECIFIC",
    [0xE4] = "ACTION_ID_228_APPL_SPECIFIC",
    [0xE5] = "ACTION_ID_229_APPL_SPECIFIC",
    [0xE6] = "ACTION_ID_230_APPL_SPECIFIC",
    [0xE7] = "ACTION_ID_231_APPL_SPECIFIC",
    [0xE8] = "ACTION_ID_232_APPL_SPECIFIC",
    [0xE9] = "ACTION_ID_233_APPL_SPECIFIC",
    [0xEA] = "ACTION_ID_234_APPL_SPECIFIC",
    [0xEB] = "ACTION_ID_235_APPL_SPECIFIC",
    [0xEC] = "ACTION_ID_236_APPL_SPECIFIC",
    [0xED] = "ACTION_ID_237_APPL_SPECIFIC",
    [0xEE] = "ACTION_ID_238_APPL_SPECIFIC",
    [0xEF] = "ACTION_ID_239_APPL_SPECIFIC",
    [0xF0] = "ACTION_ID_240_APPL_SPECIFIC",
    [0xF1] = "ACTION_ID_241_APPL_SPECIFIC",
    [0xF2] = "ACTION_ID_242_APPL_SPECIFIC",
    [0xF3] = "ACTION_ID_243_APPL_SPECIFIC",
    [0xF4] = "ACTION_ID_244_APPL_SPECIFIC",
    [0xF5] = "ACTION_ID_245_APPL_SPECIFIC",
    [0xF6] = "ACTION_ID_246_APPL_SPECIFIC",
    [0xF7] = "ACTION_ID_247_APPL_SPECIFIC",
    [0xF8] = "ACTION_ID_248_APPL_SPECIFIC",
    [0xF9] = "ACTION_ID_249_APPL_SPECIFIC",
    [0xFA] = "ACTION_ID_250_APPL_SPECIFIC",
    [0xFB] = "ACTION_ID_251_APPL_SPECIFIC",
    [0xFC] = "ACTION_ID_252_APPL_SPECIFIC",
    [0xFD] = "ACTION_ID_253_APPL_SPECIFIC",
    [0xFE] = "ACTION_ID_254_APPL_SPECIFIC",
    [0xFF] = "UNKNOWN"
}

door_state = {
    [0] = "UNKNOWN",
    [1] = "OPEN",
    [2] = "CLOSED"
}

lock_state = {
    [0] = "UNKNOWN",
    [1] = "UNLOCKED",
    [2] = "LOCKED",
    [3] = "SECURED",
    [4] = "INDETERMINATE",
    [5] = "JAMMED"
}

key_cylinder_state = {
    [0] = "UNKNOWN",
    [1] = "NOT_USED",
    [2] = "USED",
    [3] = "USED_POS_0",
    [4] = "USED_POS_1",
    [5] = "USED_POS_2"
}

handle_state = {
    [0] = "UNKNOWN",
    [1] = "NOT_USED",
    [2] = "INSIDE_USED",
    [3] = "OUTSIDE_USED",
    [4] = "BOTH_USED"
}

tamper_state = {
    [0] = "UNKNOWN",
    [1] = "NO_TAMPER",
    [2] = "TAMPER"
}

activator_state = {
    [0] = "UNKNOWN",
    [1] = "NOT_USED",
    [2] = "INSIDE_USED",
    [3] = "OUTSIDE_USED",
    [4] = "BOTH_USED",
    [5] = "UNKNOWN_SIDE_USED"
}

expire_type = {
    [0] = "RESERVED",
    [1] = "TIME",
    [2] = "USAGE_COUNT"
}


credential_management_ntf_type = {
    [0x00] = "RESERVED",
    [0x01] = "CREDENTIAL_ADDED",
    [0x02] = "SINGLE_CREDENTIAL_DELETED",
    [0x03] = "ALL_CREDENTIALS_DELETED",
    [0x04] = "ALL_DYNAMIC_CREDENTIALS_DELETED",
    [0x05] = "ALL_STATIC_CREDENTIALS_DELETED",
    [0x06] = "CREDENTIAL_DATABASE_UPDATED",
    [0x80] = "FAILED_CANT_HANDLE_MESSAGE",
    [0x81] = "FAILED_STORAGE_FULL"
}

credential_delete_mode = {
    [0] = "RESERVED",
    [1] = "DELETE_ALL_DYNAMIC",
    [2] = "DELETE_ALL_STATIC",
    [3] = "DELETE_ALL_DYNAMIC_AND_STATIC",
    [4] = "DELETE_SINGLE"
}

read_sequence = {
    [0] = "RESERVED",
    [1] = "START_NEW_READ_SEQUENCE",
    [2] = "CONTINUE_READ_SEQUENCE"
}

audit_trail_message_type = {
    [0] = "RESERVED",
    [1] = "NACK",
    [2] = "AUDIT_TRAIL_ITEM",
    [3] = "AUDIT_TRAIL_IS_EMPTY",
    [4] = "AUDIT_TRAIL_ITEMS_ADDED",
    [4] = "AUDIT_TRAIL_LOOPED"
}

access_decision = {
    [0] = "DENIED",
    [1] = "GRANTED",
    [2] = "RESERVED",
    [3] = "RESERVED",
    [4] = "ANOTHER_CREDENTIAL_REQUESTED"
}

reserved_byte = {
    [0] = "RESERVED_BYTE_VALUE_ZERO"
}


























-- Create protocol
aadp_ip_protocol = Proto("aadp_ip", "ASSA ABLOY Device Protocol for TCP/IP")
-- create field object
local field = aadp_ip_protocol.fields

field.generic_string                = ProtoField.string("generic_string", " ", base.NONE)

-- length and ID are first
field.aadp                          = ProtoField.uint16("aadp.data", "Data", base.DEC)
field.aadp_pdu_len                  = ProtoField.uint16("aadp.len", "PDU Length", base.DEC)
field.aadp_pdu_id                   = ProtoField.uint8("aadp.pdu", "PDU ID", base.DEC, pdu_names)

-- the rest in alphabetical order
field.aadp_access_decision                      = ProtoField.uint8("aadp.access_decision", "access decision", base.DEC, access_decision)
field.aadp_activator_state                      = ProtoField.uint8("aadp.activator_state", "Activator State", base.DEC, activator_state)
field.aadp_audit_trail_message_type             = ProtoField.uint8("aadp.audit_trail_message_type", "audit trail message type", base.DEC, audit_trail_message_type)
field.aadp_config_notif                         = ProtoField.uint8("aadp.config_notif", "Notification type", base.DEC, config_notification)
field.aadp_credential_bits                      = ProtoField.uint8("aadp.credential_bits", "Credential Valid Bits", base.DEC)
field.aadp_credential_block                     = ProtoField.uint16("aadp.credential_block", "Credential Block", base.DEC)
field.aadp_credential_block_type                = ProtoField.uint16("aadp.credential_block_type", "Credential Block Type", base.DEC, credential_block_type)
field.aadp_credential_data                      = ProtoField.bytes("aadp.credential_data", "Credential Data", base.NONE)
field.aadp_credential_delete_mode               = ProtoField.uint8("aadp.credential_delete_mode", "credential delete mode", base.DEC, credential_delete_mode)
field.aadp_credential_expire_limit              = ProtoField.uint32("aadp.credential_expire_limit", "Credential Expire Limit", base.DEC)
field.aadp_credential_length                    = ProtoField.uint8("aadp.credential_length", "Credential Length", base.DEC)
field.aadp_credential_management_ntf_type       = ProtoField.uint8("aadp.credential_management_ntf_type", "cred management ntf type", base.DEC, credential_management_ntf_type)
field.aadp_credential_source_details            = ProtoField.uint16("aadp.credential_source_details", "credential source details", base.HEX, credential_source_details)
field.aadp_device_definition                    = ProtoField.string("aadp.device.definition", "Device Definition", base.NONE)
field.aadp_device_id                            = ProtoField.bytes("aadp.device.device_id", "Device ID", base.NONE)
field.aadp_device_group_id                      = ProtoField.string("aadp.device_group_id", "Device Group ID", base.NONE)
field.aadp_door_default_indication              = ProtoField.bool("aadp.door.default_indication", "Default Indication", base.NONE)
field.aadp_door_mode                            = ProtoField.uint8("aadp.door_mode", "Door Mode", base.DEC, door_mode)
field.aadp_door_open                            = ProtoField.bool("aadp.door.open", "Open", base.NONE)
field.aadp_door_side                            = ProtoField.uint8("aadp.door_side", "Door Side", base.DEC, door_side)
field.aadp_door_state                           = ProtoField.uint8("aadp.door_state", "Door State", base.DEC, door_state)
field.aadp_dtc_test_completed                   = ProtoField.bool("aadp.dtc.info.test_completed", "DTC test completed", base.NONE)
field.aadp_dtc_has_failed                       = ProtoField.bool("aadp.dtc.info.has_failed", "DTC has failed", base.NONE)
field.aadp_dtc_is_failing                       = ProtoField.bool("aadp.dtc.info.is_failing", "DTC is_failing", base.NONE)
field.aadp_dtc_id                               = ProtoField.uint32("aadp.dtc.info.id", "DTC ID", base.HEX)
field.aadp_dtc_index                            = ProtoField.uint32("aadp.dtc.index", "DTC Index", base.DEC)
field.aadp_dtc_info                             = ProtoField.string("aadp.dtc.info", "DTC Info", base.NONE)
field.aadp_dtc_max_nr_dtcs_to_return            = ProtoField.uint32("aadp.dtc.max_nr_to_return", "Max nr of DTCs to return", base.DEC)
field.aadp_dtc_selection                        = ProtoField.uint8("aadp.dtc_selection", "DTC Selection", base.DEC, dtc_selection)
field.aadp_dtc_time                             = ProtoField.absolute_time("aadp.dtc.info.time", "DTC Time", ABSOLUTE_TIME_LOCAL)
field.aadp_execute_mode                         = ProtoField.uint8("aadp.execute_mode", "Execute Mode", base.DEC, execute_mode)
field.aadp_expire_type                          = ProtoField.uint8("aadp.expire_type", "Expire Type", base.DEC, expire_type)
field.aadp_handle_state                         = ProtoField.uint8("aadp.handle_state", "Handle State", base.DEC, handle_state)
field.aadp_host_device_id                       = ProtoField.bytes("aadp.device.host_device_id", "Host Device ID", base.NONE)
field.aadp_indication_type                      = ProtoField.uint8("aadp.indication.type", "Indication type", base.DEC, indication)
field.aadp_indication_type_array                = ProtoField.uint8("aadp.indication.type_array", "Indicaton type array", base.DEC)
field.aadp_key_cylinder_state                   = ProtoField.uint8("aadp.key_cylinder_state", "Key Cylinder State", base.DEC, key_cylinder_state)
field.aadp_keypad_keys                          = ProtoField.string("aadp.keypad_keys", "Keypad Keys", base.NONE)
field.aadp_location                             = ProtoField.string("aadp.location", "Location", base.NONE)
field.aadp_lock_state                           = ProtoField.uint8("aadp.lock_state", "Lock State", base.DEC, lock_state)
field.aadp_lock_platform_version                = ProtoField.uint16("aadp.lock_platform_version", "Lock Platform Version", base.DEC )
field.aadp_name                                 = ProtoField.string("aadp.name", "Name", base.NONE)
field.aadp_nr_of_appl_type_elements             = ProtoField.uint32("aadp.nr_of_appl_type_elements", "nr of appl type elements", base.DEC)
field.aadp_packet_sequence_number               = ProtoField.uint32("aadp.packet_sequence_number", "packet sequence number", base.DEC)
field.aadp_parameter_name                       = ProtoField.string("aadp.parameter.name", "parameter name", base.NONE)
field.aadp_parameter_type                       = ProtoField.uint8("aadp.parameter.type", "parameter type", base.DEC, parameter_type)
field.aadp_parameter_value_bool                 = ProtoField.bool("aadp.parameter.value.bool", "parameter value", base.NONE)
field.aadp_parameter_value_string               = ProtoField.string("aadp.parameter.value.string", "parameter value", base.NONE)
field.aadp_parameter_value_uint                 = ProtoField.uint32("aadp.parameter.value.uint", "parameter value", base.DEC)
field.aadp_parameter_value_short                = ProtoField.int16("aadp.parameter.value.short", "parameter value", base.DEC)
field.aadp_parameter_value_byte_array           = ProtoField.bytes("aadp.parameter.value.byte_array", "parameter value", base.HEX)
field.aadp_parameter_value_byte_array_len       = ProtoField.uint32("aadp.parameter.value.byte_array_len", "byte array length", base.DEC)
field.aadp_ping_interval                        = ProtoField.uint32("aadp.ping", "Ping Interval", base.DEC)
field.aadp_product_class                        = ProtoField.uint8("aadp.device.product_class", "Product Class", base.DEC, product_class)
field.aadp_raw_data                             = ProtoField.bytes("aadp.device.raw_data", "Raw data", base.NONE)
field.aadp_read_sequence                        = ProtoField.uint8("aadp.read_sequence", "read sequence", base.DEC, read_sequence)
field.aadp_reserved_byte                        = ProtoField.uint8("aadp.reserved_byte", "reserved byte", base.DEC, reserved_byte)
field.aadp_status                               = ProtoField.uint8("aadp.status", "Status", base.DEC, status)
field.aadp_string_len                           = ProtoField.uint8("aadp.string_length", "String Length", base.DEC)
field.aadp_tamper_state                         = ProtoField.uint8("aadp.tamper_state", "Tamper State", base.DEC, tamper_state)
field.aadp_time_stamp                           = ProtoField.string("aadp.time.stamp", "Time Stamp", base.NONE)
field.aadp_time_zone                            = ProtoField.int16("aadp.time.zone", "Time Zone", base.DEC)
field.aadp_timing_mode                          = ProtoField.uint8("aadp.timing.mode", "Timing - Mode", base.DEC, timing_mode)
field.aadp_timing_value                         = ProtoField.uint32("aadp.timing.value", "Timing - Time", base.DEC)
field.aadp_version_major                        = ProtoField.uint32("aadp.version.major", "Major Version", base.DEC)
field.aadp_version_minor                        = ProtoField.uint32("aadp.version.minor", "Minor Version", base.DEC)
field.aadp_version_patch                        = ProtoField.uint32("aadp.version.patch", "Patch Version", base.DEC)






local _debug_printing = true

local function debug_print(...)
    if _debug_printing then
        local msg = ""
       
        local arg = {...}
        for i,v in ipairs(arg) do
            msg = msg .. tostring(v) .. " "
        end
     
        -- msg = msg .. "\n"
        message(msg)
    end
end


-- /*** PROTOCOL TREE ***/
-- /* Now we will create a sub-tree for our protocol and start adding fields
 -- * to display under that sub-tree. Most of the time the only functions you
 -- * will need are proto_tree_add_item() and proto_item_add_subtree().
 -- *
 -- * NOTE: The offset and octetCount in the call to proto_tree_add_item()
 -- * define what data bytes to highlight in the hex display window when the
 -- * line in the protocol tree display corresponding to that item is selected.
 -- *
 -- * Supplying a octetCount of -1 tells Wireshark to highlight all data from the
 -- * offset to the end of the packet.
 -- */

-- Add ENUMS, byte arrays
local function proto_tree_add_item( tree, field_description, tvb, offset, octetCount )
    tree:add( field_description, tvb:range(offset, octetCount) )
end

-- Add strings
local function proto_tree_add_formatted_string( tree, field_description, tvb, offset, octetCount, value )
   tree:add( field_description, tvb:range(offset, octetCount), tostring(value) )
end

-- Add uintVar
local function proto_tree_add_uintvar( tree, field_description, tvb, offset, octetCount, value )
   tree:add( field_description, tvb:range(offset, octetCount), tonumber(value) )
end
-- /*
 -- * tvb-
 -- * Testy, Virtual(-izable) Buffer
 -- *
 -- * "Testy" -- the buffer gets mad when an attempt is made to access data
 -- *      beyond the bounds of the buffer. An exception is thrown.
 -- *
 -- * "Virtual" -- the buffer can have its own data, can use a subset of
 -- *      the data of a backing tvbuff, or can be a composite of
 -- *      other tvbuffs.
 -- */
function get_uintvar_from_msg_buffer( tvb, offset )
    local value         = 0
    local octet         = 0
    local uintvarLen    = 0
    local continue      = 1

    while continue ~= 0 do
        value = bit.lshift(value, 7) -- Value only exists in 7 of the 8 bits
        octet = tvb:range(offset + uintvarLen, 1):uint()
        uintvarLen = uintvarLen + 1
        value = value + bit.band(octet, 0x7F)
        continue = bit.band(octet, 0x80)
    end

    --debug_print("get_uintvar_from_msg_buffer value: " .. tostring(value))
    --debug_print("uintvarLen: " .. tostring(uintvarLen))
    return value, uintvarLen
end

function get_short_from_msg_buffer( tvb, offset )
    local value = 0
    value =  tvb:range(offset,SHORT_LEN):int()
    --debug_print("get_short_from_msg_buffer value: " .. tostring(value))
    return value, SHORT_LEN
end

function get_raw_bytes_from_msg_buffer( tvb, offset, octetCount )
    local value = 0
    value = tvb:range(offset,octetCount):uint()
    --debug_print("get_raw_bytes_from_msg_buffer value: " .. tostring(value))
    return value, octetCount
end

function get_string_from_msg_buffer( tvb, offset, octetCount )
    local value = "blank"
    value = tvb:range(offset,octetCount):string()
    --debug_print("get_string_from_msg_buffer value: " .. tostring(value))
    return value, octetCount
end

function get_enum_from_msg_buffer( tvb, offset )
    local value = 0
    value = tvb:range(offset,ENUM_LEN):uint()
    --debug_print("get_enum_from_msg_buffer value: " .. tostring(value))
    if (127 < value) or (0 > value) then
        debug_print("WARNING, enum out of range: " .. tostring(value))
    end
    return value, ENUM_LEN
end

function get_aadp_pdu_len(pinfo, tvb, offset)
    plen = tvb_get_ntohs( tvb, offset )
    return plen + 2
end















local function show_version_obj( tvb, offset, pinfo, tree, use_ping )
    local major_version = 0
    local minor_version = 0
    local patch_version = 0
    local octetCount    = 0
    local start_offset  = offset
    local ping_interval = 0

    --debug_print("offset before get major version, should be 3: " .. tostring(offset))
    octetCount = 0
    major_version, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( tree, field.aadp_version_major, tvb, offset, octetCount, major_version )
    offset = offset + octetCount
    
    --debug_print("offset before get minor version, sould be 4: " .. tostring(offset))
    octetCount = 0
    minor_version, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( tree, field.aadp_version_minor, tvb, offset, octetCount, minor_version )
    offset = offset + octetCount
    
    octetCount = 0
    patch_version, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( tree, field.aadp_version_patch, tvb, offset, octetCount, patch_version )
    offset = offset + octetCount
    
    if use_ping then
        octetCount = 0
        ping_interval, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
        proto_tree_add_uintvar( tree, field.aadp_ping_interval, tvb, offset, octetCount, ping_interval )
        offset = offset + octetCount
    end
    
    return offset - start_offset
end

function handle_device_group_id( tvb, offset, pinfo, tree )
    local octetCount    = 0
    local str_len       = 0
    
    str_len, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_item( tree, field.aadp_string_len, tvb, offset, octetCount )
    offset = offset + octetCount
    proto_tree_add_item( tree, field.aadp_device_group_id, tvb, offset, str_len )
    offset = offset + str_len
    return offset
end

function credential_pdu(tvb, offset, pinfo, tree )
    local octetCount        = 0
    local str_len           = 0
    local nr_of_blocks      = 0
    local creddata_length   = 0
    local i                 = 0
    local start_offset      = offset

    -- Get length of Device Group ID string
    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    proto_tree_add_item( tree, field.aadp_door_side, tvb, offset, ENUM_LEN )
    offset = offset + ENUM_LEN
    octetCount = 0
    
    -- Get number of credential blocks
    nr_of_blocks, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    offset = offset + octetCount
    
    -- Go through all Credential Blocks
    for i=1,nr_of_blocks,1 do
        local sub_tree = tree:add(field.aadp_credential_block_type, tvb(offset,2))
        offset = offset + 2
        proto_tree_add_item( sub_tree, field.aadp_credential_length, tvb, offset, 2 )
        creddata_length = tvb:range(offset,2):int()
        offset = offset + 2
        proto_tree_add_item( sub_tree, field.aadp_credential_bits, tvb, offset, 1 )
        offset = offset + 1
        proto_tree_add_item( sub_tree, field.aadp_credential_data, tvb, offset, creddata_length )
        offset = offset + creddata_length
    end
    
    return offset - start_offset
end

function door_config_pdu(tvb, offset, pinfo, tree )
    local octetCount
    local str_len
    local start_offset

    start_offset = offset
    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    octetCount = 0
    str_len, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_item( tree, field.aadp_string_len, tvb, offset, octetCount )
    offset = offset + octetCount
    proto_tree_add_item( tree, field.aadp_name, tvb, offset, str_len )
    offset = offset + str_len
    octetCount = 0
    str_len, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_item( tree, field.aadp_string_len, tvb, offset, octetCount )
    offset = offset + octetCount
    proto_tree_add_item( tree, field.aadp_location, tvb, offset, str_len )
    offset = offset + str_len
    proto_tree_add_item( tree, field.aadp_config_notif, tvb, offset, 1 )
    offset = offset + 1

    return offset - start_offset
end

function door_opening(tvb, offset, pinfo, tree )
    local octetCount
    local timing
    local start_offset
    
    start_offset = offset
    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    proto_tree_add_item( tree, field.aadp_door_open, tvb, offset, 1 )
    offset = offset + 1
    proto_tree_add_item( tree, field.aadp_door_default_indication, tvb, offset, 1 )
    offset = offset + 1
    proto_tree_add_item( tree, field.aadp_timing_mode, tvb, offset, 1 )
    octetCount = 0
    timing, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( tree, field.aadp_timing_value, tvb, offset, octetCount, timing )
    offset = offset + octetCount

    return offset - start_offset
end

function door_opening_result(tvb, offset, pinfo, tree )

    local start_offset
    start_offset = offset

    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    offset = handle_status(tvb, offset, tree)

    return offset - start_offset
end

function keypad_pdu(tvb, offset, pinfo, tree )

    local octetCount    = 0
    local str_len       = 0
    local start_offset  = offset
    
    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    proto_tree_add_item( tree, field.aadp_door_side, tvb, offset, ENUM_LEN )
    offset = offset + ENUM_LEN
    octetCount = 0
    str_len, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_item( tree, field.aadp_string_len, tvb, offset, octetCount )
    offset = offset + octetCount
    proto_tree_add_item( tree, field.aadp_keypad_keys, tvb, offset, str_len )
    offset = offset + str_len

    return offset - start_offset
end

function restart_pdu(tvb, offset, pinfo, tree )

    return 0
end

function indicate_pdu( tvb, offset, pinfo, tree, is_response )
    local octetCount
    local str_len
    local timing
    local i
    --proto_item *aadp_indicate_item = NULL
    --proto_tree *aadp_indicate_tree = NULL
    local start_offset
    start_offset = offset

    --/** Device Group ID */
    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    if is_response then
        --/** DopStatus */
        offset = handle_status(tvb, offset, tree)
    else
        --/** Door Side */
        proto_tree_add_item( tree, field.aadp_door_side, tvb, offset, 1 )
        offset = offset + 1
        --/** Indication[] */
        octetCount = 0
        nbr_items, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
        offset = offset + octetCount
        local sub_tree = tree:add(field.aadp_indication_type_array, nbr_items)
        --aadp_indicate_tree = proto_item_add_subtree( aadp_indicate_item, ett_aadp_indication_type_array )
        for i =1,nbr_items do
            proto_tree_add_item( sub_tree, field.aadp_indication_type, tvb, offset, 1 )
            offset = offset + 1
        end
        --/** Timing */
        proto_tree_add_item( tree, field.aadp_timing_mode, tvb, offset, 1 )
        octetCount = 0
        offset = offset + 1
        timing, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
        proto_tree_add_uintvar( tree, field.aadp_timing_value, tvb, offset, octetCount, timing )
        offset = offset + octetCount
        --/**Physical repesentation*/
        --/**add physical representation data array parser here*/

    end

    return offset - start_offset
end

function supported_doormodes_pdu(tvb, offset, pinfo, tree, is_response )
    local octetCount
    local str_len
    local i
    local start_offset
    start_offset = offset

    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    if is_response then
 
        --/** DoorAcccessMode[] */
        octetCount = 0
        nbr_items, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
        offset = offset + octetCount
        for i=1,nbr_items do
            proto_tree_add_item( tree, field.aadp_door_mode, tvb, offset, 1 )
            offset = offset + 1
        end
    end

    return offset - start_offset
end

function doormodes_pdu(tvb, offset, pinfo, tree, is_response )
    local octetCount
    local timing
    local start_offset

    start_offset = offset
    offset = handle_device_group_id( tvb, offset, pinfo, tree )

    if is_response then
        offset = handle_status(tvb, offset, tree)
    else
        --/** Door Mode */
        proto_tree_add_item( tree, field.aadp_door_mode, tvb, offset, 1 )
        offset = offset + 1
        --/** Timing - Mode*/
        proto_tree_add_item( tree, field.aadp_timing_mode, tvb, offset, 1 )
        octetCount = 0
        offset = offset + 1
        --/** Timing - Time*/
        timing, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
        proto_tree_add_uintvar( tree, field.aadp_timing_value, tvb, offset, octetCount, timing )
        offset = offset + octetCount
        --/** Execute Mode */
        proto_tree_add_item( tree, field.aadp_execute_mode, tvb, offset, 1 )
        offset = offset + 1
    end

    return offset - start_offset
end

function print_time_stamp(tree, tvb, offset, octetCount, time_stamp_secs_since_1970)
    local time_stamp_str = os.date("%d-%b-%Y  %X", time_stamp_secs_since_1970)
    proto_tree_add_formatted_string( tree, field.aadp_time_stamp, tvb, offset, octetCount, time_stamp_str )
end

function handle_time_stamp_base_year_1970(tree, tvb, offset )
    local octetCount = 0
    local time_stamp_secs_since_1970 = 0
    
    time_stamp_secs_since_1970, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    print_time_stamp(tree, tvb, offset, octetCount, time_stamp_secs_since_1970)
    
    offset = offset + octetCount
    return offset
end

function handle_time_stamp_base_year_2000(tree, tvb, offset )
    local octetCount = 0
    local time_stamp_secs_since_2000 = 0
    
    time_stamp_secs_since_2000, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    time_stamp_secs_since_1970 = SEC_FROM_1970_TO_2000 + time_stamp_secs_since_2000
    print_time_stamp(tree, tvb, offset, octetCount, time_stamp_secs_since_1970)
    
    offset = offset + octetCount
    return offset
end

function handle_time_data_type(tvb, offset, tree, aadp_pdu_len)
    local time_zone = 0
    local octetCount = 0
    time_zone_out_of_range_message = ">>>  Time Zone out of range (should be -48 to 56)  <<<"
    time_zone_bug_message          = ">>>  Time Zone bug in AH40 that uses AADP IP 1.1.0, timezone implemented as one byte, should be two  <<<"

    -- handle the timestamp
    offset = handle_time_stamp_base_year_2000(tree, tvb, offset)
    
    -- handle the timezone
    -- handle bug in Aperio AH40 AADP IP 1.1.0 Timezone implemented as signed char (signed int 8)
    if ( aadp_pdu_len + PDU_LEN_LEN - TIME_ZONE_LEN/2 ) == offset then -- one byte from end of PDU, Timezone is one byte
        time_zone = tvb(offset, TIME_ZONE_LEN/2 ):int()
        proto_tree_add_item( tree, field.aadp_time_zone, tvb, offset, TIME_ZONE_LEN/2 )
        proto_tree_add_formatted_string(tree, field.generic_string, tvb, OFFSET_ZERO, octetCount, time_zone_bug_message)
        offset = offset + TIME_ZONE_LEN/2
    else
        time_zone = tvb(offset, TIME_ZONE_LEN):int()
        if (   ( time_zone < TIME_ZONE_MIN ) or ( time_zone > TIME_ZONE_MAX )   ) then
            proto_tree_add_item( tree, field.aadp_time_zone, tvb, offset, TIME_ZONE_LEN, time_zone )
            proto_tree_add_formatted_string(tree, field.generic_string, tvb, OFFSET_ZERO, octetCount, time_zone_out_of_range_message)
        else
            proto_tree_add_item( tree, field.aadp_time_zone, tvb, offset, TIME_ZONE_LEN )
        end
        offset = offset + TIME_ZONE_LEN
    end
    return offset
end

function handle_device_id(tvb, offset, tree)
    proto_tree_add_item( tree, field.aadp_device_id, tvb, offset, DEVICE_ID_LEN )
    offset = offset + DEVICE_ID_LEN
    return offset
end

function handle_response_status(tvb, offset, tree)
    offset = handle_status(tvb, offset, tree)
    return offset
end

function handle_lock_version(tvb, offset, tree)
    proto_tree_add_item( tree, field.aadp_lock_platform_version, tvb, offset, LOCK_VERSION_LEN )
    offset = offset + LOCK_VERSION_LEN
    return offset
end

function handle_parameter_name(tvb, offset, tree)
    local octetCount = 0
    local str_len = 0
    local parameter_name = "blank"
    str_len, octetCount = get_uintvar_from_msg_buffer( tvb, offset )
    proto_tree_add_item( tree, field.aadp_string_len, tvb, offset, STR_LEN_LEN )
    offset = offset + STR_LEN_LEN --offset w size of str_len

    parameter_name = get_string_from_msg_buffer( tvb, offset, str_len )
    proto_tree_add_item( tree, field.aadp_parameter_name, tvb, offset, str_len )
    
    offset = offset + str_len

    return offset, parameter_name
end

function handle_parameter_type( tvb, offset, tree )
    local octetCount = 0
    local parameter_type = 0xFF;
    parameter_type, octetCount = get_uintvar_from_msg_buffer( tvb, offset )
    proto_tree_add_item( tree, field.aadp_parameter_type, tvb, offset, PARAMETER_TYPE_LEN )
    offset = offset + PARAMETER_TYPE_LEN
    return offset, parameter_type
end

function handle_parameter_value( tvb, offset, tree, parameter_type, parameter_name )
    local parameter_value_uint = 0
    local parameter_value_short = 0
    local byte_array_len = 0
    local str_len = 0
    local octetCount = 0
    local result = RES_ERROR
    if parameter_type == 0 then -- UINTVAR
       parameter_value_uint, octetCount = get_uintvar_from_msg_buffer( tvb, offset, octetCount )
       proto_tree_add_uintvar( tree, field.aadp_parameter_value_uint, tvb, offset, octetCount, parameter_value_uint )
       offset = offset + octetCount
       result = RES_OK
    elseif parameter_type == 1 then -- BOOLEAN
       proto_tree_add_item( tree, field.aadp_parameter_value_bool, tvb, offset, BOOL_LEN )
       offset = offset + BOOL_LEN
       result = RES_OK
    elseif parameter_type == 2 then -- STRING
       str_len, octetCount = get_uintvar_from_msg_buffer( tvb, offset, octetCount )
       offset = offset + octetCount--size of string length
       proto_tree_add_item( tree, field.aadp_parameter_value_string, tvb, offset, str_len )
       offset = offset + str_len
       result = RES_OK
    elseif parameter_type == 3 then -- SHORT
       parameter_value_short, octetCount = get_short_from_msg_buffer( tvb, offset, octetCount )
       proto_tree_add_item( tree, field.aadp_parameter_value_short, tvb, offset, SHORT_LEN, parameter_value_short )
       offset = offset + octetCount
       result = RES_OK
    -- param type == 4 is reserved 
    elseif parameter_type == 5 then -- BYTE_ARRAY
        --debug_print("parameter_name: " .. tostring(parameter_name))
        byte_array_len, octetCount = get_uintvar_from_msg_buffer( tvb, offset, octetCount )
        proto_tree_add_item( tree, field.aadp_parameter_value_byte_array_len, tvb, offset, octetCount )
        offset = offset + octetCount -- octetCount is size of byte array length
        if "LockVersions" == tostring(parameter_name) then
            offset = handle_device_id(tvb, offset, tree)
            offset = handle_lock_version(tvb, offset, tree)
            result = RES_OK
        else
            proto_tree_add_item( tree, field.aadp_parameter_value_byte_array, tvb, offset, byte_array_len )
            offset = offset + byte_array_len
            result = RES_OK
        end
    else
        proto_tree_add_formatted_string(tree, field.generic_string, tvb, OFFSET_ZERO, octetCount, "ERROR: No parameter type match, no parameter read")
        proto_tree_add_formatted_string(tree, field.generic_string, tvb, OFFSET_ZERO, octetCount, "Look at raw data in Packet Bytes View")
        result = RES_ERROR
    end
    return offset, result
end

function get_device_parameter_command_pdu( tvb, offset, tree, aadp_pdu_len )
    offset = handle_device_id(tvb, offset, tree)
    while offset < ( aadp_pdu_len + PDU_LEN_LEN ) do
        offset = handle_parameter_name(tvb, offset, tree)
    end
    return offset
end

function get_device_parameter_result_pdu( tvb, offset, tree, aadp_pdu_len )
    local parameter_type = 0xFF
    local octetCount = 0
    local parameter_name = "blank"
    local result = RES_ERROR
    offset = handle_device_id(tvb, offset, tree)
    while offset < ( aadp_pdu_len + PDU_LEN_LEN ) do
        proto_tree_add_formatted_string(tree, field.generic_string, tvb, OFFSET_ZERO, octetCount, ": : : : : : : : : : : : : : : : : : : : : : : : :")
        offset, parameter_name = handle_parameter_name(tvb, offset, tree)
        offset, parameter_type = handle_parameter_type(tvb, offset, tree )
        offset, result = handle_parameter_value(tvb, offset, tree, parameter_type, parameter_name)
        if RES_ERROR == result then
            break
        end
        if offset < ( aadp_pdu_len + PDU_LEN_LEN ) then -- at end of PDU
            -- response status was introduced in AADP IP 1.2.0
            offset = handle_response_status(tvb, offset, tree)
        end
    end
    return offset
end

function show_dtc( tvb, offset, pinfo, tree )
    local start_offset  = 0
    local octetCount    = 0
    local dtc_id        = 0
    local dtc_time      = 0
    local time_stamp    = 0
    start_offset = offset
    
    -- create sub-tree for the DTC info
    local sub_tree = tree:add(field.aadp_dtc_info)
    
    -- handle the dtc id
    dtc_id, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( sub_tree, field.aadp_dtc_id, tvb, offset, octetCount, dtc_id )
    offset = offset + octetCount
    
    -- handle the timestamp
    offset = handle_time_stamp_base_year_1970(sub_tree, tvb, offset )

    -- handle DTC flags
    proto_tree_add_item( sub_tree, field.aadp_dtc_is_failing, tvb, offset, BOOL_LEN )
    offset = offset + BOOL_LEN
    proto_tree_add_item( sub_tree, field.aadp_dtc_has_failed, tvb, offset, BOOL_LEN )
    offset = offset + BOOL_LEN
    proto_tree_add_item( sub_tree, field.aadp_dtc_test_completed, tvb, offset, BOOL_LEN )
    offset = offset + BOOL_LEN

    return offset - start_offset
end

function show_device( tvb, offset, pinfo, tree )
    local start_offset
    start_offset = offset
    
    --/** Own DeviceID */
    local device_id = tvb(offset, DEVICE_ID_LEN)
    local sub_tree = tree:add(field.aadp_device_id, device_id)
    offset = offset + DEVICE_ID_LEN
    
    --/** Host DeviceID */
    proto_tree_add_item( sub_tree, field.aadp_host_device_id, tvb, offset, DEVICE_ID_LEN )
    offset = offset + DEVICE_ID_LEN
    --/** Product class */
    proto_tree_add_item( sub_tree, field.aadp_product_class, tvb, offset, 1)
    offset = offset + 1

    return offset - start_offset
end

function dtc_notification_pdu( tvb, offset, pinfo, tree )
    local start_offset
    start_offset = offset

    --/** DeviceID */
    proto_tree_add_item( tree, field.aadp_device_id, tvb, offset, DEVICE_ID_LEN )
    offset = offset + DEVICE_ID_LEN

    offset = offset + show_dtc( tvb, offset, pinfo, tree )

    return offset - start_offset
end

function dtc_status_pdu(tvb, offset, pinfo, tree, is_response )
    local octetCount
    local str_len
    local page_size
    local index
    local i
    local start_offset
    start_offset = offset

    --/** DeviceID */
    proto_tree_add_item( tree, field.aadp_device_id, tvb, offset, DEVICE_ID_LEN )
    offset = offset + DEVICE_ID_LEN

    if ( is_response ) then
        --/** DTCInfo[]*/
        octetCount = 0

        offset, nr_of_elements = handle_number_of_application_type_elements(tvb, offset, tree )
        
        for i=1,nr_of_elements do
 
            offset = offset + show_dtc( tvb, offset, pinfo, tree )
        end
    else
        --/** DTCSelection */
        proto_tree_add_item( tree, field.aadp_dtc_selection, tvb, offset, 1 )
        offset = offset + 1
        --/** Max nr of DTCs to return, previously called Page Size */
        octetCount = 0
        page_size, octetCount = get_uintvar_from_msg_buffer( tvb, offset, octetCount )
        proto_tree_add_uintvar( tree, field.aadp_dtc_max_nr_dtcs_to_return, tvb, offset, octetCount, page_size )
        offset = offset + octetCount
        --/** Index */
        octetCount = 0
        index, octetCount = get_uintvar_from_msg_buffer( tvb, offset, octetCount )
        proto_tree_add_uintvar( tree, field.aadp_dtc_index, tvb, offset, octetCount, index )
        offset = offset + octetCount
    end

    return offset - start_offset
end

function clear_dtc_pdu(tvb, offset, pinfo, tree, is_response )
    local start_offset
    local octetCount
    local dtc_id


    --(void) pinfo

    start_offset = offset

    --/** DeviceID */
    proto_tree_add_item( tree, field.aadp_device_id, tvb, offset, DEVICE_ID_LEN)
    offset = offset + DEVICE_ID_LEN

    if ( is_response ) then
        offset = handle_status(tvb, offset, tree)
    else
        --/** DTC id */
        octetCount = 0
        dtc_id, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
        proto_tree_add_uintvar( tree, field.aadp_dtc_id, tvb, offset, octetCount, dtc_id )
        offset = offset + octetCount
    end

    return offset - start_offset
end

function device_list_pdu( tvb, offset, pinfo, tree, is_response )
    local octetCount
    local str_len
    local i
    local start_offset
    start_offset = offset

    if ( is_response ) then
        --/** DeviceDefinition[]*/
        octetCount = 0
        nbr_pdu_items, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
        offset = offset + octetCount
        for i=1,nbr_pdu_items do
            offset = offset + show_device( tvb, offset, pinfo, tree )
        end
    else
        --/** No input parameter for command*/
    end

    return offset - start_offset
end

function time_notification_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_time_data_type(tvb, offset, tree, aadp_pdu_len)
    return offset
end

function set_time_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_time_data_type(tvb, offset, tree, aadp_pdu_len)
    return offset
end

function set_time_result_pdu( tvb, offset, pinfo, tree )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_response_status(tvb, offset, tree)
    return offset
end

function get_time_command_pdu( tvb, offset, pinfo, tree )
    offset = handle_device_id(tvb, offset, tree)
    return offset
end

function get_time_result_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_time_data_type(tvb, offset, tree, aadp_pdu_len)
    return offset
end

function set_device_parameter_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    local parameter_type
    offset = handle_device_id(tvb, offset, tree)
    --debug_print("( aadp_pdu_len + SHORT_LEN ) before while in set_device_parameter_command_pdu: " .. tostring(( aadp_pdu_len + SHORT_LEN )))
    --debug_print("offset: " .. tostring(offset))
    while offset < ( aadp_pdu_len + SHORT_LEN ) do
        offset = handle_parameter_name(tvb, offset, tree )
        offset, parameter_type = handle_parameter_type(tvb, offset, tree )
        offset = handle_parameter_value(tvb, offset, tree, parameter_type )
        --debug_print("offset at end of while in set_device_parameter_command_pdu: " .. tostring(offset))
    end
    return offset
end

function set_device_parameter_result_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    offset = handle_device_id(tvb, offset, tree)
    --debug_print("( aadp_pdu_len + SHORT_LEN ) before while in set_device_parameter_result_pdu: " .. tostring(( aadp_pdu_len + SHORT_LEN )))
    --debug_print("offset: " .. tostring(offset))
    while offset < ( aadp_pdu_len + SHORT_LEN ) do
        offset = handle_parameter_name(tvb, offset, tree)
        offset = handle_response_status(tvb, offset, tree)
        --debug_print("offset at end of while in set_device_parameter_result_pdu: " .. tostring(offset))
    end
    return offset
end

function device_parameter_notification_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    local parameter_type
    offset = handle_device_id(tvb, offset, tree)
    --debug_print("( aadp_pdu_len + SHORT_LEN ) before while in device_parameter_notification_pdu: " .. tostring(( aadp_pdu_len + SHORT_LEN )))
    --debug_print("offset: " .. tostring(offset))
    while offset < ( aadp_pdu_len + SHORT_LEN ) do
        offset = handle_parameter_name(tvb, offset, tree )
        offset, parameter_type = handle_parameter_type(tvb, offset, tree )
        offset = handle_parameter_value(tvb, offset, tree, parameter_type )
        offset = handle_response_status(tvb, offset, tree)
        --debug_print("offset at end of while in device_parameter_notification_pdu: " .. tostring(offset))
    end
    return offset
end






























function handle_packet_sequence_number(tvb, offset, tree)
    local packet_sequence_number = 0
    local octetCount = 0
    packet_sequence_number, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( tree, field.aadp_packet_sequence_number, tvb, offset, octetCount, packet_sequence_number )
    return offset + octetCount
end

function handle_reserved_byte(tvb, offset, tree)
    local reserved_byte_value = 0
    reserved_byte_value, octetCount = get_raw_bytes_from_msg_buffer( tvb, offset, BYTE_LEN )
    proto_tree_add_item( tree, field.aadp_reserved_byte, tvb, offset, octetCount)--, reserved_byte_value )
    return offset + octetCount
end

function handle_expire_type(tvb, offset, tree)
    local expire_type_value = 0
    expire_type_value, octetCount = get_enum_from_msg_buffer( tvb, offset )
    proto_tree_add_item( tree, field.aadp_expire_type, tvb, offset, octetCount )
    --debug_print("handle_expire_type : " .. tostring(expire_type_value))
    return offset + octetCount
end

function handle_credential_expire_limit(tvb, offset, tree)
    local credential_expire_limit_value = 0
    local octetCount = 0
    credential_expire_limit_value, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( tree, field.aadp_credential_expire_limit, tvb, offset, octetCount, credential_expire_limit_value )
    --debug_print("handle_credential_expire_limit : " .. tostring(credential_expire_limit_value))
    return offset + octetCount
end

function handle_number_of_application_type_elements(tvb, offset, tree )
    local nr_of_elements = 0
    local octetCount = 0
    nr_of_elements, octetCount = get_uintvar_from_msg_buffer( tvb, offset)
    proto_tree_add_uintvar( tree, field.aadp_nr_of_appl_type_elements, tvb, offset, octetCount, nr_of_elements )
    return offset + octetCount, nr_of_elements
end

function handle_credential_management_ntf_type(tvb, offset, tree)
    local credential_management_ntf_type_value = 0
    credential_management_ntf_type_value, octetCount = get_enum_from_msg_buffer( tvb, offset )
    proto_tree_add_item( tree, field.aadp_credential_management_ntf_type, tvb, offset, octetCount )
    return offset + octetCount
end

function handle_credential_delete_mode(tvb, offset, tree)
    local credential_delete_mode_value = 0
    credential_delete_mode_value, octetCount = get_enum_from_msg_buffer( tvb, offset )
    proto_tree_add_item( tree, field.aadp_credential_delete_mode, tvb, offset, octetCount )
    return offset + octetCount, credential_delete_mode_value
end

function handle_source_details( tvb, offset, tree )
    --local credential_source_details_value = 0
    --credential_source_details_value, octetCount = get_raw_bytes_from_msg_buffer( tvb, offset, CREDENTIAL_SOURCE_DETAILS_LEN )
    proto_tree_add_item( tree, field.aadp_credential_source_details, tvb, offset, CREDENTIAL_SOURCE_DETAILS_LEN )
    return offset + CREDENTIAL_SOURCE_DETAILS_LEN
end

function handle_credential_block(tvb, offset, tree)
    local credential_block_type_value = get_raw_bytes_from_msg_buffer( tvb, offset, CRED_BLOCK_TYPE_LEN )
    --debug_print("credential_block_type_value: " .. tostring(credential_block_type_value))
    local sub_tree = tree:add(field.aadp_credential_block_type, tvb(offset,CRED_BLOCK_TYPE_LEN))
    local SOURCE_DETAILS = 0x0100
    offset = offset + CRED_BLOCK_TYPE_LEN
    proto_tree_add_item( sub_tree, field.aadp_credential_length, tvb, offset, 2 )
    creddata_length = tvb:range(offset,2):int()
    offset = offset + 2
    proto_tree_add_item( sub_tree, field.aadp_credential_bits, tvb, offset, 1 )
    offset = offset + 1
    --debug_print("SOURCE_DETAILS: " .. tostring(SOURCE_DETAILS))
    --debug_print("credential_block_type_value: " .. tostring(credential_block_type_value))
    if SOURCE_DETAILS == credential_block_type_value then
        offset = handle_source_details( tvb, offset, sub_tree )
        --debug_print("handle source details has been called: " .. tostring(offset))
    else
        proto_tree_add_item( sub_tree, field.aadp_credential_data, tvb, offset, creddata_length )
        offset = offset + creddata_length
    end

    return offset
end

function handle_door_mode(tvb, offset, tree)
    proto_tree_add_item( tree, field.aadp_door_mode, tvb, offset, ENUM_LEN ) 
    offset = offset + ENUM_LEN
    return offset
end

function handle_read_sequence(tvb, offset, tree)
    proto_tree_add_item( tree, field.aadp_read_sequence, tvb, offset, ENUM_LEN ) 
    offset = offset + ENUM_LEN
    return offset
end

function handle_status(tvb, offset, tree)
    proto_tree_add_item( tree, field.aadp_status, tvb, offset, ENUM_LEN )
    offset = offset + ENUM_LEN
    return offset
end

function handle_audit_trail_message_type(tvb, offset, tree)
    proto_tree_add_item( tree, field.aadp_audit_trail_message_type, tvb, offset, ENUM_LEN )
    offset = offset + ENUM_LEN
    return offset
end

function handle_access_decision(tree, tvb, offset)
    proto_tree_add_item( tree, field.aadp_access_decision, tvb, offset, ENUM_LEN )
    offset = offset + ENUM_LEN
    return offset
end


























function set_credential_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_packet_sequence_number(tvb, offset, tree)
    offset = handle_reserved_byte(tvb, offset, tree)
    offset = handle_expire_type(tvb, offset, tree)
    offset = handle_credential_expire_limit(tvb, offset, tree)
    while offset < ( aadp_pdu_len + SHORT_LEN ) do
        offset, nr_of_elements = handle_number_of_application_type_elements(tvb, offset, tree )
        for i=1,nr_of_elements,1 do
            offset = handle_credential_block(tvb, offset, tree)
        end
    end
    return offset
end

function set_credential_result_pdu( tvb, offset, pinfo, tree )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_response_status(tvb, offset, tree)
end

function delete_credential_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    local credential_delete_mode = 0
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_packet_sequence_number(tvb, offset, tree)
    offset = handle_reserved_byte(tvb, offset, tree)
    offset, credential_delete_mode_value = handle_credential_delete_mode(tvb, offset, tree)
    if credential_delete_mode_value == 4 then
        offset, nr_of_elements = handle_number_of_application_type_elements(tvb, offset, tree )
        for i=1,nr_of_elements,1 do
            offset = handle_credential_block(tvb, offset, tree)
        end
    end
    return offset
end

function delete_credential_result_pdu( tvb, offset, pinfo, tree )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_response_status(tvb, offset, tree)
end

function credential_management_notification_pdu ( tvb, offset, pinfo, tree )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_packet_sequence_number(tvb, offset, tree)
    offset = handle_reserved_byte(tvb, offset, tree)
    offset = handle_credential_management_ntf_type(tvb, offset, tree)
    return offset
end

function get_door_mode_command_pdu( tvb, offset, pinfo, tree )
    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    return offset
end

function get_door_mode_result_pdu ( tvb, offset, pinfo, tree )
    offset = handle_device_group_id( tvb, offset, pinfo, tree )
    offset = handle_door_mode(tvb, offset, tree)
    return offset
end

function get_audit_trail_command_pdu( tvb, offset, pinfo, tree )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_packet_sequence_number(tvb, offset, tree)
    offset = handle_reserved_byte(tvb, offset, tree)
    offset = handle_read_sequence(tvb, offset, tree)
    return offset
end

function get_audit_trail_result_pdu( tvb, offset, pinfo, tree )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_status(tvb, offset, tree)
    return offset
end

function audit_trail_notification_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
    offset = handle_device_id(tvb, offset, tree)
    offset = handle_packet_sequence_number(tvb, offset, tree)
    offset = handle_reserved_byte(tvb, offset, tree)
    offset = handle_audit_trail_message_type(tvb, offset, tree)
    offset = handle_time_stamp_base_year_2000(tree, tvb, offset)
    offset = handle_access_decision(tree, tvb, offset)
    while offset < ( aadp_pdu_len + SHORT_LEN ) do
        offset, nr_of_elements = handle_number_of_application_type_elements(tvb, offset, tree )
        for i=1,nr_of_elements,1 do
              offset = handle_credential_block(tvb, offset, tree)
        end
    end
end


-- Below is the "main()" function
do

    -- present plugin version number in wireshark menu Help > About Wireshark > Plugins
    -- only version is used by wireshark 2.0.3
    local my_info = { version = "1.4.0",
                      author = "Gustav Blix and Stefan Johansson",
                      description = "Assa Abloy Device Protocol - IP, Dissector",
                      repository = "https://www.assaabloy.com"
                    }
    set_plugin_info(my_info)

    local function aadp_ip_dissector(tvb, pinfo, tree)
        debug_print("checking frame: " .. tostring(pinfo.number) .. ": " .. tostring(tvb))

        local offset = 0
        local aadp_pdu_len = 0
        local aadp_pdu_id = 0
        local buffer_length = 0
        local info = "" -- the info string printed in the info column in the GUI
        pinfo.cols['info'] = info -- print the info to the GUI
        local nr_of_pdus = 0
        local not_used_variable = 0
        local continue = 1
        local octetCount = 0


        --/*** HEURISTICS ***/
        --/* First, if at all possible, do some heuristics to check if the packet
        --* cannot possibly belong to your protocol.  
        --*/
        
        -- get and sanity check length of PDU
        buffer_length = ( tvb:len() )
        debug_print("buffer_length: " .. tostring(buffer_length))

        -- /* create display subtree for the protocol */
        -- add the AADP protocol tree
        tree = tree:add(aadp_ip_protocol, tvb() )
        
        --/* Set the Protocol column to the constant string of aadp */
        pinfo.cols.protocol = PROTO_TAG_AADP

        aadp_pdu_len, octetCount = get_short_from_msg_buffer(tvb, offset)
        -- if buffer_length < (aadp_pdu_len + PDU_LEN_LEN) then
          -- pinfo.cols['info'] = "AADP IP ERROR: PDU length field > PDU buffer size"  
          -- return 0
        -- end
        if aadp_pdu_len < PROTOCOL_DATA_UNIT_MIN_LENGTH then
          pinfo.cols['info'] = "AADP IP ERROR: PDU length < PROTOCOL_DATA_UNIT_MIN_LENGTH"
          return 0
        end
        if aadp_pdu_len > PROTOCOL_DATA_UNIT_MAX_LENGTH then
          pinfo.cols['info'] = "AADP IP ERROR: PDU length > PROTOCOL_DATA_UNIT_MAX_LENGTH"
          return 0
        end

        -- display raw data 
        proto_tree_add_item( tree, field.aadp_raw_data, tvb, (offset), (aadp_pdu_len + PDU_LEN_LEN) )
        offset = offset + octetCount --add offset for aadp_pdu_len
        
        -- display PDU length
        proto_tree_add_item( tree, field.aadp_pdu_len, tvb, OFFSET_ZERO, PDU_LEN_LEN )
        
        -- get and sanity check PDU ID
        aadp_pdu_id, octetCount =  get_uintvar_from_msg_buffer(tvb, offset)
        offset = offset + octetCount
        if aadp_pdu_id > 127 then
            pinfo.cols['info'] = "AADP IP ERROR: PDU ID > 127"
            return 0
        end
        if aadp_pdu_id == 0 then
            pinfo.cols['info'] = "AADP IP ERROR: PDU ID == 0"
            return 0
        end
        -- display PDU name and ID
        proto_tree_add_item( tree, field.aadp_pdu_id, tvb, OFFSET_ZERO + PDU_LEN_LEN, PDU_ID_LEN )

        info = pdu_names[aadp_pdu_id] 
        pinfo.cols['info'] = info .. " (" .. aadp_pdu_id .. ")"  
        debug_print("aadp_pdu_id: " .. tostring(aadp_pdu_id))

        if aadp_pdu_id == 1 then
          -- /* HelloRequest */
          offset = offset + show_version_obj( tvb, offset, pinfo, tree, false )
        elseif aadp_pdu_id == 2 then
          offset = offset + show_version_obj( tvb, offset, pinfo, tree, true )
        elseif aadp_pdu_id == 3 then
          offset = offset + door_config_pdu( tvb, offset, pinfo, tree )
        elseif aadp_pdu_id == 5 then
          offset = offset + credential_pdu( tvb, offset, pinfo, tree )
        elseif aadp_pdu_id == 6 then
          offset = offset + keypad_pdu( tvb, offset, pinfo, tree )
        elseif aadp_pdu_id == 7 then
          offset = offset + restart_pdu( tvb, offset, pinfo, tree )
        elseif ( aadp_pdu_id == 8 or aadp_pdu_id == 9 ) then
          offset = offset + indicate_pdu( tvb, offset, pinfo, tree, aadp_pdu_id == 9 )
        elseif ( aadp_pdu_id == 10 or aadp_pdu_id == 11 ) then
          offset = offset + supported_doormodes_pdu( tvb, offset, pinfo, tree, aadp_pdu_id == 11 )
        elseif aadp_pdu_id == 12 then
          offset = offset + door_opening( tvb, offset, pinfo, tree )
        elseif aadp_pdu_id == 13 then
          offset = offset + door_opening_result( tvb, offset, pinfo, tree )
        elseif ( aadp_pdu_id == 15 or aadp_pdu_id == 16 ) then
          offset = offset + doormodes_pdu( tvb, offset, pinfo, tree, aadp_pdu_id == 16 )
        elseif ( aadp_pdu_id == 17 or aadp_pdu_id == 18 ) then
          offset = handle_status(tvb, offset, tree)
        elseif aadp_pdu_id == 19 then
          offset = get_device_parameter_command_pdu( tvb, offset, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 20 then
          offset = get_device_parameter_result_pdu( tvb, offset, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 21 then
          offset = offset + dtc_notification_pdu( tvb, offset, pinfo, tree )
        elseif ( aadp_pdu_id == 22 or aadp_pdu_id == 23 ) then
          offset = offset + dtc_status_pdu( tvb, offset, pinfo, tree, aadp_pdu_id == 23 )
        elseif ( aadp_pdu_id == 24 or aadp_pdu_id == 25 ) then
          offset = offset + clear_dtc_pdu( tvb, offset, pinfo, tree, aadp_pdu_id == 25 )
        elseif ( aadp_pdu_id == 26 or aadp_pdu_id == 27 ) then
          offset = offset + device_list_pdu( tvb, offset, pinfo, tree, aadp_pdu_id == 27 )
        elseif aadp_pdu_id == 30 then
          offset = get_audit_trail_command_pdu( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 31 then
          offset = get_audit_trail_result_pdu( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 32 then
          offset = audit_trail_notification_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 40 then
          offset = time_notification_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 41 then
          offset = set_time_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 42 then
          offset = set_time_result_pdu( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 43 then
          offset = get_time_command_pdu( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 44 then
          offset = get_time_result_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 50 then
          offset = set_device_parameter_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 51 then
          offset = set_device_parameter_result_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 52 then
          offset = device_parameter_notification_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 55 then
          offset = set_credential_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 56 then
          offset = set_credential_result_pdu( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 57 then
          offset = delete_credential_command_pdu( tvb, offset, pinfo, tree, aadp_pdu_len )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 58 then
          offset = delete_credential_result_pdu( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 59 then
          offset = credential_management_notification_pdu ( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 64 then -- getDoorState
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
        elseif ( aadp_pdu_id == 65 or aadp_pdu_id == 66 ) then
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
          proto_tree_add_item( tree, field.aadp_door_state, tvb, offset, 1 )
          offset = offset + 1
        elseif aadp_pdu_id == 68 then -- getLockSTate
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
        elseif ( aadp_pdu_id == 69 or aadp_pdu_id == 70 ) then
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
          proto_tree_add_item( tree, field.aadp_lock_state, tvb, offset )
          offset = offset + 1
        elseif aadp_pdu_id == 72 then -- getKeyCylinderState
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
        elseif ( aadp_pdu_id == 73 or aadp_pdu_id == 74 ) then
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
          proto_tree_add_item( tree, field.aadp_key_cylinder_state, tvb, offset, 1 )
          offset = offset + 1
        elseif aadp_pdu_id == 76 then -- getHandleState
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
        elseif ( aadp_pdu_id == 77 or aadp_pdu_id == 78 ) then
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
          proto_tree_add_item( tree, field.aadp_handle_state, tvb, offset, 1 )
          offset = offset + 1
        elseif aadp_pdu_id == 80 then -- getTamperState
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
        elseif( aadp_pdu_id == 81 or aadp_pdu_id == 82 ) then
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
          proto_tree_add_item( tree, field.aadp_tamper_state, tvb, offset, 1 ) 
          offset = offset + 1
        elseif aadp_pdu_id == 84 then -- getActivatorState
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
        elseif( aadp_pdu_id == 85 or aadp_pdu_id == 86 ) then
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
          proto_tree_add_item( tree, field.aadp_activator_state, tvb, offset, 1 ) 
          offset = offset + 1
        elseif aadp_pdu_id == 88 then
          offset = get_door_mode_command_pdu ( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 89 then
          offset = get_door_mode_result_pdu ( tvb, offset, pinfo, tree )
          --  note: offset is replaced by return value, not added like other functions..
        elseif aadp_pdu_id == 90 then -- doorModeNotification
          offset = handle_device_group_id( tvb, offset, pinfo, tree )
          proto_tree_add_item( tree, field.aadp_door_mode, tvb, offset, 1 ) 
          offset = offset + 1
        end

        pinfo.cols.protocol = aadp_ip_protocol.name -- print protocol name in the protocol column in the GUI

    end

    -- register what function is the actual dissector
    aadp_ip_protocol.dissector = aadp_ip_dissector 

    -- in Wireshark, register AADP_IP in the TCP subdissector table (Decode as..)
    local all_ports = -1
    local tcp_table_handle = DissectorTable.get("tcp.port")
    tcp_table_handle:add( all_ports, aadp_ip_protocol )
end